]> Git Repo - qemu.git/blob - scripts/qemu.py
target/i386: [tcg] Port to breakpoint_check
[qemu.git] / scripts / qemu.py
1 # QEMU library
2 #
3 # Copyright (C) 2015-2016 Red Hat Inc.
4 # Copyright (C) 2012 IBM Corp.
5 #
6 # Authors:
7 #  Fam Zheng <[email protected]>
8 #
9 # This work is licensed under the terms of the GNU GPL, version 2.  See
10 # the COPYING file in the top-level directory.
11 #
12 # Based on qmp.py.
13 #
14
15 import errno
16 import string
17 import os
18 import sys
19 import subprocess
20 import qmp.qmp
21
22
23 class QEMUMachine(object):
24     '''A QEMU VM
25
26     Use this object as a context manager to ensure the QEMU process terminates::
27
28         with VM(binary) as vm:
29             ...
30         # vm is guaranteed to be shut down here
31     '''
32
33     def __init__(self, binary, args=[], wrapper=[], name=None, test_dir="/var/tmp",
34                  monitor_address=None, socket_scm_helper=None, debug=False):
35         if name is None:
36             name = "qemu-%d" % os.getpid()
37         if monitor_address is None:
38             monitor_address = os.path.join(test_dir, name + "-monitor.sock")
39         self._monitor_address = monitor_address
40         self._qemu_log_path = os.path.join(test_dir, name + ".log")
41         self._popen = None
42         self._binary = binary
43         self._args = list(args) # Force copy args in case we modify them
44         self._wrapper = wrapper
45         self._events = []
46         self._iolog = None
47         self._socket_scm_helper = socket_scm_helper
48         self._debug = debug
49
50     def __enter__(self):
51         return self
52
53     def __exit__(self, exc_type, exc_val, exc_tb):
54         self.shutdown()
55         return False
56
57     # This can be used to add an unused monitor instance.
58     def add_monitor_telnet(self, ip, port):
59         args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
60         self._args.append('-monitor')
61         self._args.append(args)
62
63     def add_fd(self, fd, fdset, opaque, opts=''):
64         '''Pass a file descriptor to the VM'''
65         options = ['fd=%d' % fd,
66                    'set=%d' % fdset,
67                    'opaque=%s' % opaque]
68         if opts:
69             options.append(opts)
70
71         self._args.append('-add-fd')
72         self._args.append(','.join(options))
73         return self
74
75     def send_fd_scm(self, fd_file_path):
76         # In iotest.py, the qmp should always use unix socket.
77         assert self._qmp.is_scm_available()
78         if self._socket_scm_helper is None:
79             print >>sys.stderr, "No path to socket_scm_helper set"
80             return -1
81         if os.path.exists(self._socket_scm_helper) == False:
82             print >>sys.stderr, "%s does not exist" % self._socket_scm_helper
83             return -1
84         fd_param = ["%s" % self._socket_scm_helper,
85                     "%d" % self._qmp.get_sock_fd(),
86                     "%s" % fd_file_path]
87         devnull = open('/dev/null', 'rb')
88         p = subprocess.Popen(fd_param, stdin=devnull, stdout=sys.stdout,
89                              stderr=sys.stderr)
90         return p.wait()
91
92     @staticmethod
93     def _remove_if_exists(path):
94         '''Remove file object at path if it exists'''
95         try:
96             os.remove(path)
97         except OSError as exception:
98             if exception.errno == errno.ENOENT:
99                 return
100             raise
101
102     def is_running(self):
103         return self._popen and (self._popen.returncode is None)
104
105     def exitcode(self):
106         if self._popen is None:
107             return None
108         return self._popen.returncode
109
110     def get_pid(self):
111         if not self.is_running():
112             return None
113         return self._popen.pid
114
115     def _load_io_log(self):
116         with open(self._qemu_log_path, "r") as fh:
117             self._iolog = fh.read()
118
119     def _base_args(self):
120         if isinstance(self._monitor_address, tuple):
121             moncdev = "socket,id=mon,host=%s,port=%s" % (
122                 self._monitor_address[0],
123                 self._monitor_address[1])
124         else:
125             moncdev = 'socket,id=mon,path=%s' % self._monitor_address
126         return ['-chardev', moncdev,
127                 '-mon', 'chardev=mon,mode=control',
128                 '-display', 'none', '-vga', 'none']
129
130     def _pre_launch(self):
131         self._qmp = qmp.qmp.QEMUMonitorProtocol(self._monitor_address, server=True,
132                                                 debug=self._debug)
133
134     def _post_launch(self):
135         self._qmp.accept()
136
137     def _post_shutdown(self):
138         if not isinstance(self._monitor_address, tuple):
139             self._remove_if_exists(self._monitor_address)
140         self._remove_if_exists(self._qemu_log_path)
141
142     def launch(self):
143         '''Launch the VM and establish a QMP connection'''
144         devnull = open('/dev/null', 'rb')
145         qemulog = open(self._qemu_log_path, 'wb')
146         try:
147             self._pre_launch()
148             args = self._wrapper + [self._binary] + self._base_args() + self._args
149             self._popen = subprocess.Popen(args, stdin=devnull, stdout=qemulog,
150                                            stderr=subprocess.STDOUT, shell=False)
151             self._post_launch()
152         except:
153             if self.is_running():
154                 self._popen.kill()
155                 self._popen.wait()
156             self._load_io_log()
157             self._post_shutdown()
158             raise
159
160     def shutdown(self):
161         '''Terminate the VM and clean up'''
162         if self.is_running():
163             try:
164                 self._qmp.cmd('quit')
165                 self._qmp.close()
166             except:
167                 self._popen.kill()
168
169             exitcode = self._popen.wait()
170             if exitcode < 0:
171                 sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode, ' '.join(self._args)))
172             self._load_io_log()
173             self._post_shutdown()
174
175     underscore_to_dash = string.maketrans('_', '-')
176     def qmp(self, cmd, conv_keys=True, **args):
177         '''Invoke a QMP command and return the result dict'''
178         qmp_args = dict()
179         for k in args.keys():
180             if conv_keys:
181                 qmp_args[k.translate(self.underscore_to_dash)] = args[k]
182             else:
183                 qmp_args[k] = args[k]
184
185         return self._qmp.cmd(cmd, args=qmp_args)
186
187     def command(self, cmd, conv_keys=True, **args):
188         reply = self.qmp(cmd, conv_keys, **args)
189         if reply is None:
190             raise Exception("Monitor is closed")
191         if "error" in reply:
192             raise Exception(reply["error"]["desc"])
193         return reply["return"]
194
195     def get_qmp_event(self, wait=False):
196         '''Poll for one queued QMP events and return it'''
197         if len(self._events) > 0:
198             return self._events.pop(0)
199         return self._qmp.pull_event(wait=wait)
200
201     def get_qmp_events(self, wait=False):
202         '''Poll for queued QMP events and return a list of dicts'''
203         events = self._qmp.get_events(wait=wait)
204         events.extend(self._events)
205         del self._events[:]
206         self._qmp.clear_events()
207         return events
208
209     def event_wait(self, name, timeout=60.0, match=None):
210         # Test if 'match' is a recursive subset of 'event'
211         def event_match(event, match=None):
212             if match is None:
213                 return True
214
215             for key in match:
216                 if key in event:
217                     if isinstance(event[key], dict):
218                         if not event_match(event[key], match[key]):
219                             return False
220                     elif event[key] != match[key]:
221                         return False
222                 else:
223                     return False
224
225             return True
226
227         # Search cached events
228         for event in self._events:
229             if (event['event'] == name) and event_match(event, match):
230                 self._events.remove(event)
231                 return event
232
233         # Poll for new events
234         while True:
235             event = self._qmp.pull_event(wait=timeout)
236             if (event['event'] == name) and event_match(event, match):
237                 return event
238             self._events.append(event)
239
240         return None
241
242     def get_log(self):
243         return self._iolog
This page took 0.037635 seconds and 4 git commands to generate.