mirror of
https://github.com/QubesOS/qubes-doc.git
synced 2025-05-02 06:46:11 -04:00
Move all developer documentation to main index
QubesOS/qubes-issues#2070
This commit is contained in:
parent
254f5e71a0
commit
aadb35d62f
39 changed files with 46 additions and 3 deletions
138
debugging/automated-tests.md
Normal file
138
debugging/automated-tests.md
Normal file
|
@ -0,0 +1,138 @@
|
|||
---
|
||||
layout: doc
|
||||
title: Automated Tests
|
||||
permalink: /doc/automated-tests/
|
||||
redirect_from:
|
||||
- /en/doc/automated-tests/
|
||||
- /doc/AutomatedTests/
|
||||
---
|
||||
|
||||
Automatic tests
|
||||
===============
|
||||
|
||||
Starting with Qubes R3 we use [python unittest][unittest] to perform automatic
|
||||
tests of Qubes OS. Regardless of the name, we use it for both [unit
|
||||
tests](https://en.wikipedia.org/wiki/Unit_tests) and [integration
|
||||
tests](https://en.wikipedia.org/wiki/Integration_tests). The main purpose is of
|
||||
course to deliver much more stable releases.
|
||||
|
||||
Integration tests are written with assumption to be called on dedicated
|
||||
hardware. **Do not run those test on machine where you have important data, you
|
||||
can loose it**. Especially all the VMs with name starting with `test-` are
|
||||
removed. All the tests are started from dom0, even when testing some VM
|
||||
component. Those tests will create new VM(s), run the test, then remove the VM(s).
|
||||
|
||||
Most of the tests are stored in [core-admin
|
||||
repository](https://github.com/QubesOS/qubes-core-admin/tree/master/tests) in
|
||||
`tests` directory. To start them you can use standard python unittest runner:
|
||||
python -m unittest -v qubes.tests
|
||||
Or our custom one:
|
||||
python -m qubes.tests.run -v
|
||||
|
||||
Our test runner can be used mostly the same as the standard one, with some nice
|
||||
additional features like no need to prefix all the tests with "qubes.tests", or
|
||||
color output. It is also the only way to run only selected template tests.
|
||||
|
||||
You can use `python -m qubes.tests.run -h` to get usage information:
|
||||
|
||||
[user@dom0 ~]$ python -m qubes.tests.run -h
|
||||
usage: run.py [-h] [--verbose] [--quiet] [--list] [--failfast] [--no-failfast]
|
||||
[--do-not-clean] [--do-clean] [--loglevel LEVEL]
|
||||
[--logfile FILE] [--syslog] [--no-syslog] [--kmsg] [--no-kmsg]
|
||||
[TESTNAME [TESTNAME ...]]
|
||||
|
||||
positional arguments:
|
||||
TESTNAME list of tests to run named like in description
|
||||
(default: run all tests)
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--verbose, -v increase console verbosity level
|
||||
--quiet, -q decrease console verbosity level
|
||||
--list, -l list all available tests and exit
|
||||
--failfast, -f stop on the first fail, error or unexpected success
|
||||
--no-failfast disable --failfast
|
||||
--do-not-clean, --dnc, -D
|
||||
do not execute tearDown on failed tests. Implies
|
||||
--failfast.
|
||||
--do-clean, -C do execute tearDown even on failed tests.
|
||||
--loglevel LEVEL, -L LEVEL
|
||||
logging level for file and syslog forwarding (one of:
|
||||
NOTSET, DEBUG, INFO, WARN, WARNING, ERROR, CRITICAL;
|
||||
default: DEBUG)
|
||||
--logfile FILE, -o FILE
|
||||
if set, test run will be also logged to file
|
||||
--syslog reenable logging to syslog
|
||||
--no-syslog disable logging to syslog
|
||||
--kmsg, --very-brave-or-very-stupid
|
||||
log most important things to kernel ring-buffer
|
||||
--no-kmsg, --i-am-smarter-than-kay-sievers
|
||||
do not abuse kernel ring-buffer
|
||||
|
||||
When running only specific tests, write their names like in log, in format:
|
||||
MODULE+"/"+CLASS+"/"+FUNCTION. MODULE should omit initial "qubes.tests.".
|
||||
Example: basic/TC_00_Basic/test_000_create
|
||||
|
||||
For example to run only tests for fedora-21 template, you can use `-l` option, then filter the list:
|
||||
|
||||
[user@dom0 ~]$ python -m qubes.tests.run -l | grep fedora-21
|
||||
network/VmNetworking_fedora-21/test_000_simple_networking
|
||||
network/VmNetworking_fedora-21/test_010_simple_proxyvm
|
||||
network/VmNetworking_fedora-21/test_020_simple_proxyvm_nm
|
||||
network/VmNetworking_fedora-21/test_030_firewallvm_firewall
|
||||
network/VmNetworking_fedora-21/test_040_inter_vm
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_000_start_shutdown
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_010_run_gui_app
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_050_qrexec_simple_eof
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_051_qrexec_simple_eof_reverse
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_052_qrexec_vm_service_eof
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_053_qrexec_vm_service_eof_reverse
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_060_qrexec_exit_code_dom0
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_065_qrexec_exit_code_vm
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_100_qrexec_filecopy
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_110_qrexec_filecopy_deny
|
||||
vm_qrexec_gui/TC_00_AppVM_fedora-21/test_120_qrexec_filecopy_self
|
||||
vm_qrexec_gui/TC_20_DispVM_fedora-21/test_000_prepare_dvm
|
||||
vm_qrexec_gui/TC_20_DispVM_fedora-21/test_010_simple_dvm_run
|
||||
vm_qrexec_gui/TC_20_DispVM_fedora-21/test_020_gui_app
|
||||
vm_qrexec_gui/TC_20_DispVM_fedora-21/test_030_edit_file
|
||||
[user@dom0 ~]$ python -m qubes.tests.run -v `python -m qubes.tests.run -l | grep fedora-21`
|
||||
|
||||
Example test run:
|
||||
|
||||

|
||||
|
||||
## Adding a new test to core-admin
|
||||
After you added a new unit test to [core-admin/tests](https://github.com/QubesOS/qubes-core-admin/tree/master/tests)
|
||||
you have to make sure of two things:
|
||||
|
||||
1. The test will be added to the RPM file created by [QubesBuilder](/doc/qubes-builder/)
|
||||
For this you need to edit [core-admin/tests/Makefile](https://github.com/QubesOS/qubes-core-admin/tree/master/tests/Makefile)
|
||||
2. The test will be loaded by [core-admin/tests/\_\_init\_\_.py](https://github.com/QubesOS/qubes-core-admin/tree/master/tests/__init__.py)
|
||||
|
||||
### Editing the Makefile
|
||||
Add at the bottom of the file the two lines which will copy your test and its
|
||||
compiled version to the right directory in the RPM file. I.e. adding `example.py`
|
||||
|
||||
cp example.py $(DESTDIR)$(PYTHON_TESTSPATH)
|
||||
cp example.py[co] $(DESTDIR)$(PYTHON_TESTSPATH)
|
||||
|
||||
|
||||
### Editing \_\_init\_\_.py
|
||||
Add at the bottom of the file in the method `def load_tests` to the variable
|
||||
`modname` your test. I.e adding `example.py`.
|
||||
|
||||
~~~python
|
||||
for modname in (
|
||||
'qubes.tests.basic',
|
||||
'qubes.tests.dom0_update',
|
||||
'qubes.tests.network',
|
||||
'qubes.tests.vm_qrexec_gui',
|
||||
'qubes.tests.backup',
|
||||
'qubes.tests.backupcompatibility',
|
||||
'qubes.tests.regressions',
|
||||
'qubes.tests.example', # This is our newly added test
|
||||
):
|
||||
~~~
|
||||
|
||||
[unittest]: https://docs.python.org/2/library/unittest.html
|
98
debugging/profiling.md
Normal file
98
debugging/profiling.md
Normal file
|
@ -0,0 +1,98 @@
|
|||
---
|
||||
layout: doc
|
||||
title: Profiling
|
||||
permalink: /doc/profiling/
|
||||
redirect_from:
|
||||
- /en/doc/profiling/
|
||||
- /doc/Profiling/
|
||||
- /wiki/Profiling/
|
||||
---
|
||||
|
||||
Profiling
|
||||
=========
|
||||
|
||||
This is python profiling primer.
|
||||
|
||||
For the purpose of this document, `qubes-dev` is name of the domain used for postprocessing profiling stats.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
~~~
|
||||
yum install gprof2dot graphviz
|
||||
git clone http://git.woju.eu/qubes/profiling.git
|
||||
~~~
|
||||
|
||||
If you profile something on dom0, move `Upload.sh` from repository to dom0:
|
||||
|
||||
~~~
|
||||
mkdir -p ~/profiling
|
||||
qvm-run -p qubes-dev 'cat ~/profiling/Upload.sh' > ~/profiling/Upload.sh
|
||||
~~~
|
||||
|
||||
- WARNING: this will obviously be running third party code which is not signed by ITL nor Fedora. You have been warned.
|
||||
|
||||
Workflow
|
||||
--------
|
||||
|
||||
### Identify function responsible for some slow action
|
||||
|
||||
You have to select area in which you suspect less than optimal performance. If you do not narrow the area, graphs may be unreadable.
|
||||
|
||||
### Replace suspect function with probe
|
||||
|
||||
Replace
|
||||
|
||||
def foo(self, bar):
|
||||
# function content
|
||||
|
||||
with
|
||||
|
||||
def foo(self, *args, **kwargs):
|
||||
profile.runctx('self.real_foo(*args, **kwargs)', globals(), locals(),
|
||||
time.strftime('/home/user/profiling/foo-%Y%m%d-%H%M%S.pstats'))
|
||||
|
||||
def real_foo(self, bar):
|
||||
# function content
|
||||
|
||||
### Run application
|
||||
|
||||
Beware that some functions may be called often. For example `qubesmanager/main.py:update_table` gets run once per second. This will produce one pstat file per second.
|
||||
|
||||
Remember to revert your changes to application afterwards.
|
||||
|
||||
### Upload statistics
|
||||
|
||||
If you are in dom0:
|
||||
|
||||
~~~
|
||||
cd ~/profiling
|
||||
./Upload.sh
|
||||
~~~
|
||||
|
||||
### Analyse
|
||||
|
||||
~~~
|
||||
make
|
||||
~~~
|
||||
|
||||
For every `${basename}.pstats` this will produce `${basename}.txt` and `${basename}.svg`. SVG contains call graph. Text file contains list of all functions sorted by cumulative execution time. You may also try `make all-png`.
|
||||
|
||||
~~~
|
||||
make index.html
|
||||
~~~
|
||||
|
||||
This creates `index.html` with all SVG graphics linked to TXT files. Ready for upload.
|
||||
|
||||
~~~
|
||||
make REMOTE=example.com:public_html/qubes/profiling/ upload
|
||||
~~~
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
This example is from `qubes-manager` (`qubesmanager/main.py`).
|
||||
|
||||

|
||||
|
||||
It is apparent than problem is around `get_disk_usage` which calls something via `subprocess.call`. It does it 15 times, probably once per VM.
|
156
debugging/test-bench.md
Normal file
156
debugging/test-bench.md
Normal file
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
layout: doc
|
||||
title: Test Bench
|
||||
permalink: /doc/test-bench/
|
||||
redirect_from:
|
||||
- /en/doc/test-bench/
|
||||
- /doc/TestBench/
|
||||
- /wiki/TestBench/
|
||||
---
|
||||
|
||||
Test bench for Dom0
|
||||
===================
|
||||
|
||||
This guide shows how to set up simple test bench that automatically test your code you're about to push. It is written especially for `core3` branch of `core-admin.git` repo, but some ideas are universal.
|
||||
|
||||
We will set up a spare machine (bare metal, not a virtual) that will be hosting our experimental Dom0. We will communicate with it via Ethernet and SSH. This tutorial assumes you are familiar with [QubesBuilder](/doc/qubes-builder/) and you have it set up and running flawlessly.
|
||||
|
||||
Setting up the machine
|
||||
----------------------
|
||||
|
||||
First, do a clean install from ISO you built or grabbed elsewhere.
|
||||
|
||||
You have to fix network, because it is intentionally broken. This script should reenable your network card without depending on anything else.
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
# adjust this for your NIC (run lspci)
|
||||
BDF=0000:02:00.0
|
||||
|
||||
prog=$(basename $0)
|
||||
|
||||
pciunbind() {
|
||||
local path
|
||||
path=/sys/bus/pci/devices/${1}/driver/unbind
|
||||
if ! [ -w ${path} ]; then
|
||||
echo "${prog}: Device ${1} not bound"
|
||||
return 1
|
||||
fi
|
||||
echo -n ${1} >${path}
|
||||
}
|
||||
|
||||
pcibind() {
|
||||
local path
|
||||
path=/sys/bus/pci/drivers/${2}/bind
|
||||
if ! [ -w ${path} ]; then
|
||||
echo "${prog}: Driver ${2} not found"
|
||||
return 1
|
||||
fi
|
||||
echo ${1} >${path}
|
||||
}
|
||||
|
||||
pciunbind ${BDF}
|
||||
pcibind ${BDF} e1000e
|
||||
|
||||
dhclient
|
||||
|
||||
TODO: describe how to run this at every startup
|
||||
|
||||
Now configure your DHCP server so your testbench gets static IP and connect your machine to your local network. You should ensure that your testbench can reach the Internet.
|
||||
|
||||
Install `openssh-server` on your testbench:
|
||||
|
||||
~~~
|
||||
yum install openssh-server
|
||||
~~~
|
||||
|
||||
Ensure that sudo works without password from your user account (it should by default).
|
||||
|
||||
Development VM
|
||||
--------------
|
||||
|
||||
### SSH
|
||||
|
||||
Arrange firewall so you can reach the testbench from your `qubes-dev` VM. Generate SSH key in `qubes-dev`:
|
||||
|
||||
~~~
|
||||
ssh-keygen -t ecdsa -b 521
|
||||
~~~
|
||||
|
||||
Add the following section in `.ssh/config` in `qubes-dev`:
|
||||
|
||||
~~~
|
||||
Host testbench
|
||||
# substitute username in testbench
|
||||
User user
|
||||
# substitute address of your testbench
|
||||
HostName 192.168.123.45
|
||||
~~~
|
||||
|
||||
Then connect to your testbench and paste newly generated `id_ecdsa.pub` to `.ssh/authorized_keys` on testbench so you can log in without entering password every time.
|
||||
|
||||
### Scripting
|
||||
|
||||
This step is optional, but very helpful. Put these scripts somewhere in your `${PATH}`, like `/usr/local/bin`.
|
||||
|
||||
`qtb-runtests`:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
ssh testbench python -m qubes.tests.run
|
||||
|
||||
`qtb-install`:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
TMPDIR=/tmp/qtb-rpms
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "usage: $(basename $0) <rpmfile> ..."
|
||||
exit 2
|
||||
fi
|
||||
|
||||
set -e
|
||||
|
||||
ssh testbench mkdir -p "${TMPDIR}"
|
||||
scp "${@}" testbench:"${TMPDIR}"
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
ssh testbench sudo rpm -i --replacepkgs --replacefiles "${TMPDIR}/$(basename ${1})"
|
||||
shift
|
||||
done
|
||||
|
||||
`qtb-iterate`:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# substitute path to your builder installation
|
||||
pushd ${HOME}/builder >/dev/null
|
||||
|
||||
# the following are needed only if you have sources outside builder
|
||||
#rm -rf qubes-src/core-admin
|
||||
#make COMPONENTS=core-admin get-sources
|
||||
|
||||
make core-admin
|
||||
qtb-install qubes-src/core-admin/rpm/x86_64/qubes-core-dom0-*.rpm
|
||||
qtb-runtests
|
||||
|
||||
### Hooking git
|
||||
|
||||
I (woju) have those two git hooks. They ensure tests are passing (or are marked as expected failure) when committing and pushing. For committing it is only possible to run tests that may be executed from git repo (even if the rest were available, I probably wouldn't want to do that). For pushing, I also install RPM and run tests on testbench.
|
||||
|
||||
`core-admin/.git/hooks/pre-commit`: (you may retain also the default hook, here omitted for readability)
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
python -c "import sys, qubes.tests.run; sys.exit(not qubes.tests.run.main())"
|
||||
|
||||
`core-admin/.git/hooks/pre-push`:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
exec qtb-iterate
|
129
debugging/vm-interface.md
Normal file
129
debugging/vm-interface.md
Normal file
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
layout: doc
|
||||
title: VM Configuration Interface
|
||||
permalink: /doc/vm-interface/
|
||||
redirect_from:
|
||||
- /en/doc/vm-interface/
|
||||
- /doc/VMInterface/
|
||||
- /doc/SystemDoc/VMInterface/
|
||||
- /wiki/SystemDoc/VMInterface/
|
||||
---
|
||||
|
||||
VM Configuration Interface
|
||||
==========================
|
||||
|
||||
Qubes VM have some settings set by dom0 based on VM settings. There are multiple configuration channels, which includes:
|
||||
|
||||
- XenStore
|
||||
- QubesDB - replacing most of xenstore (in R3 only)
|
||||
- Qubes RPC (called at VM startup, or when configuration changed)
|
||||
- GUI protocol
|
||||
|
||||
xenstore
|
||||
--------
|
||||
|
||||
Keys exposed by dom0 to VM (only Qubes specific included):
|
||||
|
||||
- `qubes-vm-type` - VM type, the same as `type` field in `qvm-prefs`. One of `AppVM`, `ProxyVM`, `NetVM`, `TemplateVM`, `HVM`, `TemplateHVM`
|
||||
- `qubes-vm-updatable` - flag whether VM is updatable (whether changes in root.img will survive VM restart). One of `True`, `False`
|
||||
- `qubes-timezone - name of timezone based on dom0 timezone. For example `Europe/Warsaw`
|
||||
- `qubes-keyboard` - keyboard layout based on dom0 layout. Its syntax is suitable for `xkbcomp` command (after expanding escape sequences like `\n` or `\t`). This is meant only as some default value, VM can ignore this option and choose its own keyboard layout (this is what keyboard setting from Qubes Manager does). This entry is created as part of gui-daemon initialization (so not available when gui-daemon disabled, or not started yet).
|
||||
- `qubes-debug-mode` - flag whether VM have debug mode enabled (qvm-prefs setting). One of `1`, `0`
|
||||
- `qubes-service/SERVICE_NAME` - subtree for VM services controlled from dom0 (using qvm-service command or Qubes Manager). One of `1`, `0`. Note that not every service will be listed here, if entry is missing, it means "use VM default". List of currently supported services is in [qvm-service man page](/wiki/Dom0Tools/QvmService)
|
||||
- `qubes-netmask` - network mask (only when VM has netvm set); currently hardcoded "255.255.255.0"
|
||||
- \`qubes-ip - IP address for this VM (only when VM has netvm set)
|
||||
- `qubes-gateway` - default gateway IP and primary DNS address (only when VM has netvm set); VM should add host route to this address directly via eth0 (or whatever default interface name is)
|
||||
- `qubes-secondary-dns` - secondary DNS address (only when VM has netvm set)
|
||||
- `qubes-netvm-gateway` - same as `qubes-gateway` in connected VMs (only when VM serves as network backend - ProxyVM and NetVM); because this is also set as primary DNS in connected VMs, traffic sent to this IP on port 53 should be redirected to DNS server
|
||||
- `qubes-netvm-netmask` - same as `qubes-netmask` in connected VMs (only when VM serves as network backend - ProxyVM and NetVM)
|
||||
- `qubes-netvm-network` - network address (only when VM serves as network backend - ProxyVM and NetVM); can be also calculated from qubes-netvm-gateway and qubes-netvm-netmask
|
||||
- `qubes-netvm-secondary-dns` - same as `qubes-secondary-dns` in connected VMs (only when VM serves as network backend - ProxyVM and NetVM); traffic sent to this IP on port 53 should be redirected to secondary DNS server
|
||||
|
||||
Keys set by VM for passing info to dom0:
|
||||
|
||||
- `memory/meminfo` - used memory (updated by qubes-meminfo-writer), input information for qmemman; Format: 6 lines (EOL encoded as `\n`), each in format "FIELD: VALUE kB"; fields: `MemTotal`, `MemFree`, `Buffers`, `Cached`, `SwapTotal`, `SwapFree`; meaning the same as in `/proc/meminfo` in Linux
|
||||
- `qubes-block-devices` - list of block devices exposed by this VM, each device (subdirectory) should be named in a way that VM can attach the device based on it. Each should contain those entries:
|
||||
- `desc` - device description (ASCII text)
|
||||
- `size` - device size in bytes
|
||||
- `mode` - default connection mode; `r` for read-only, `w` for read-write
|
||||
- `qubes-usb-devices` - list of USB devices exposed by this VM, each device (subdirectory) should contain:
|
||||
- `desc` - device description (ASCII text)
|
||||
- `usb-ver` - USB version (1, 2 or 3)
|
||||
|
||||
Qubes RPC
|
||||
---------
|
||||
|
||||
Services called by dom0 to provide some VM configuration:
|
||||
|
||||
- `qubes.SetMonitorLayout` - provide list of monitors, one in a line, each line contains four numbers: `width height X Y`
|
||||
- `qubes.WaitForSession` - called to wait for full VM startup
|
||||
- `qubes.GetAppmenus` - receive appmenus from given VM (template); TODO: describe format here
|
||||
- `qubes.GetImageRGBA` - receive image/application icon. Protocol:
|
||||
|
||||
1. Caller sends name of requested icon. This can be one of:
|
||||
* `xdgicon:NAME` - search for NAME in standard icons theme
|
||||
* `-` - get icon data from stdin (the caller), can be prefixed with format name, for example `png:-`
|
||||
* file name
|
||||
2. The service responds with image dimensions: width and height as
|
||||
decimal numbers, separated with space and with EOL marker at the and; then
|
||||
image data in RGBA format (32 bits per pixel)
|
||||
- `qubes.SetDateTime` - set VM time, called periodically by dom0 (can be
|
||||
triggered manually from dom0 by calling `qvm-sync-clock`). The service
|
||||
receives one line at stdin - time in format of `date -u -Iseconds`, for
|
||||
example `2015-07-31T16:10:43+0000`.
|
||||
- `qubes.SetGuiMode` - called in HVM to switch between fullscreen and seamless
|
||||
GUI mode. The service receives a single word on stdin - either `FULLSCREEN`
|
||||
or `SEAMLESS`
|
||||
|
||||
|
||||
Other Qrexec services installed by default:
|
||||
|
||||
- `qubes.Backup` - store Qubes backup. The service receives location chosen by
|
||||
the user (one line, terminated by '\n'), the backup archive ([description of
|
||||
backup format](/doc/BackupEmergencyRestoreV2/))
|
||||
- `qubes.DetachPciDevice` - service called in reaction to `qvm-pci -d` call on
|
||||
running VM. The service receives one word - BDF of device to detach. When the
|
||||
service call ends, the device will be detached
|
||||
- `qubes.Filecopy` - receive some files from other VM. Files sent in [qfile format](/doc/qfilecopy/)
|
||||
- `qubes.OpenInVM` - open a file in called VM. Service receives a single file on stdin (in
|
||||
[qfile format](/doc/qfilecopy/). After a file viewer/editor is terminated, if
|
||||
the file was modified, can be sent back (just raw content, without any
|
||||
headers); otherwise service should just terminate without sending anything.
|
||||
This service is used by both `qvm-open-in-vm` and `qvm-open-in-dvm` tools. When
|
||||
called in DispVM, service termination will trigger DispVM cleanup.
|
||||
- `qubes.Restore` - retrieve Qubes backup. The service receives backup location
|
||||
entered by the user (one line, terminated by '\n'), then should output backup
|
||||
archive in [qfile format](/doc/qfilecopy/) (core-agent-linux component contains
|
||||
`tar2qfile` utility to do the conversion
|
||||
- `qubes.SelectDirectory`, `qubes.SelectFile` - services which should show
|
||||
file/directory selection dialog and return (to stdout) a single line
|
||||
containing selected path, or nothing in case of cancellation
|
||||
- `qubes.SuspendPre` - service called in every VM with PCI device attached just
|
||||
before system suspend
|
||||
- `qubes.SuspendPost` - service called in every VM with PCI device attached just
|
||||
after system resume
|
||||
- `qubes.SyncNtpClock` - service called to trigger network time synchronization.
|
||||
Service should synchronize local VM time and terminate when done.
|
||||
- `qubes.WindowIconUpdater` - service called by VM to send icons of individual
|
||||
windows. The protocol there is simple one direction stream: VM sends window ID
|
||||
followed by icon in `qubes.GetImageRGBA` format, then next window ID etc. VM
|
||||
can send icon for the same window multiple times to replace previous one (for
|
||||
example for animated icons)
|
||||
- `qubes.VMShell` - call any command in the VM; the command(s) is passed one per line
|
||||
|
||||
Currently Qubes still calls few tools in VM directly, not using service
|
||||
abstraction. This will change in the future. Those tools are:
|
||||
|
||||
- `/usr/lib/qubes/qubes-download-dom0-updates.sh` - script to download updates (or new packages to be installed) for dom0 (`qubes-dom0-update` tool)
|
||||
- `date -u -Iseconds` - called directly to retrieve time after calling `qubes.SyncNtpClock` service (`qvm-sync-clock` tool)
|
||||
- `nm-online -x` - called before `qubes.SyncNtpClock` service call by `qvm-sync-clock` tool
|
||||
- `resize2fs` - called to resize filesystem on /rw partition by `qvm-grow-private` tool
|
||||
- `gpk-update-viewer` - called by Qubes Manager to display available updates in a TemplateVM
|
||||
- `systemctl start qubes-update-check.timer` (and similarly stop) - called when enabling/disabling updates checking in given VM (`qubes-update-check` [qvm-service](/doc/qubes-service/))
|
||||
|
||||
Additionally automatic tests extensively calls various commands directly in VMs. We do not plan to change that.
|
||||
|
||||
GUI protocol
|
||||
------------
|
||||
|
||||
GUI initialization includes passing the whole screen dimensions from dom0 to VM. This will most likely be overwritten by qubes.SetMonitorLayout Qubes RPC call.
|
213
debugging/windows-debugging.md
Normal file
213
debugging/windows-debugging.md
Normal file
|
@ -0,0 +1,213 @@
|
|||
---
|
||||
layout: doc
|
||||
title: Windows Debugging
|
||||
permalink: /doc/windows-debugging/
|
||||
redirect_from:
|
||||
- /en/doc/windows-debugging/
|
||||
- /doc/WindowsDebugging/
|
||||
- /wiki/WindowsDebugging/
|
||||
---
|
||||
|
||||
Debugging Windows HVMs
|
||||
======================
|
||||
|
||||
Debugging Windows code can be tricky in a virtualized environment. The guide below assumes Xen hypervisor and Windows 7 VMs.
|
||||
|
||||
User-mode debugging is usually straightforward if it can be done on one machine. Just duplicate your normal debugging environment in the VM.
|
||||
|
||||
Things get complicated if you need to perform kernel debugging or troubleshoot problems that only manifest on system boot, user logoff or similar. For that you need two Windows VMs: the *host* and the *target*. The *host* will contain [WinDbg](http://msdn.microsoft.com/en-us/library/windows/hardware/ff551063(v=vs.85).aspx) installation, your source code and private symbols. The *target* will run the code being debugged. Both will be linked by virtual serial ports.
|
||||
|
||||
- First, you need to prepare separate copies of both *target* and *host* VM configuration files with some changes. Copy the files from **/var/lib/qubes/appvms/vmname/vmname.conf** to some convenient location, let's call them **host.conf** and **target.conf**.
|
||||
- In both copied files add the following line at the end: `serial = 'pty'`. This will make Xen connect VM's serial ports to dom0's ptys.
|
||||
- From now on you need to start both VMs like this: `qvm-start --custom-config=/your/edited/host.conf host`
|
||||
- To connect both VM serial ports together you will either need [socat](http://www.dest-unreach.org/socat/) or a custom utility described later.
|
||||
- To determine which dom0 pty corresponds to VM's serial port you need to read xenstore, example script below:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
id1=$(xl domid "$1-dm")
|
||||
tty1=$(xenstore-read /local/domain/${id1}/device/console/3/tty)
|
||||
echo $tty1
|
||||
|
||||
Pass it a running VM name and it will output the corresponding pty name.
|
||||
|
||||
- To connect both ptys you can use [socat](http://www.dest-unreach.org/socat/) like that:
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
id1=$(xl domid "$1-dm")
|
||||
id2=$(xl domid "$2-dm")
|
||||
tty1=$(xenstore-read /local/domain/${id1}/device/console/3/tty)
|
||||
tty2=$(xenstore-read /local/domain/${id2}/device/console/3/tty)
|
||||
socat $tty1,raw $tty2,raw
|
||||
|
||||
...but there is a catch. Xen seems to process the traffic that goes through serial ports and changes all **0x0a** bytes into **0x0d, 0x0a** pairs (newline conversion). I didn't find a way to turn that off (setting ptys to raw mode didn't change anything) and it's not mentioned anywhere on the Internet, so maybe it's something on my system. If the above script works for you then you don't need anything more in dom0.
|
||||
|
||||
- On the *target* system, run `bcdedit /set debug on` on the console to turn on kernel debugging. It defaults to the first serial port.
|
||||
- On the *host* system, install [WinDbg](http://msdn.microsoft.com/en-us/library/windows/hardware/ff551063(v=vs.85).aspx) and start the kernel debug (Ctrl-K), choose **com1** as the debug port.
|
||||
- Reboot the *target* VM.
|
||||
- Run the above shell script in dom0.
|
||||
- If everything is fine you should see the proper kernel debugging output in WinDbg. However, if you see something like that:
|
||||
|
||||
~~~
|
||||
Opened \\.\com1
|
||||
Waiting to reconnect...
|
||||
Connected to Windows 7 7601 x64 target at (Wed Mar 19 20:35:43.262 2014 (UTC + 1:00)), ptr64 TRUE
|
||||
Kernel Debugger connection established.
|
||||
Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols
|
||||
Executable search path is:
|
||||
... Retry sending the same data packet for 64 times.
|
||||
The transport connection between host kernel debugger and target Windows seems lost.
|
||||
please try resync with target, recycle the host debugger, or reboot the target Windows.
|
||||
Unable to read KTHREAD address fffff8000281ccc0
|
||||
**************************************************************************
|
||||
Unable to read debugger data block header
|
||||
**************************************************************************
|
||||
Unable to read KTHREAD address fffff8000281ccc0
|
||||
Unable to read PsLoadedModuleList
|
||||
Unable to read KTHREAD address fffff8000281ccc0
|
||||
**************************************************************************
|
||||
Unable to read debugger data block header
|
||||
**************************************************************************
|
||||
~~~
|
||||
|
||||
...then you're most likely a victim of the CRLF issue mentioned above. To get around it I wrote a small utility that basically does what socat would do and additionally corrects those replaced bytes in the stream. It's not pretty but it works:
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
|
||||
int fd1, fd2;
|
||||
char mark = ' ';
|
||||
|
||||
void out(unsigned char c)
|
||||
{
|
||||
static int count = 0;
|
||||
static unsigned char buf[17] = {0};
|
||||
|
||||
// relay to ouptput port
|
||||
write(fd2, &c, 1);
|
||||
fprintf(stderr, "%c", mark);
|
||||
|
||||
/* dump all data going over the line
|
||||
if (count == 0)
|
||||
fprintf(stderr, "%c", mark);
|
||||
fprintf(stderr, "%02x ", c);
|
||||
if (c >= 0x20 && c < 0x80)
|
||||
buf[count] = c;
|
||||
else
|
||||
buf[count] = '.';
|
||||
count++;
|
||||
if (count == 0x10)
|
||||
{
|
||||
count = 0;
|
||||
fprintf(stderr, " %s\n", buf);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
unsigned char c = 0;
|
||||
struct termios tio;
|
||||
ssize_t size;
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s pty1 pty2 [mark character]\n", argv[0]);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
fd1 = open(argv[1], O_RDONLY | O_NOCTTY);
|
||||
if (fd1 <= 0)
|
||||
{
|
||||
perror("open fd1");
|
||||
return errno;
|
||||
}
|
||||
fd2 = open(argv[2], O_WRONLY | O_NOCTTY);
|
||||
if (fd2 <= 0)
|
||||
{
|
||||
perror("open fd2");
|
||||
return errno;
|
||||
}
|
||||
/*
|
||||
// This doesn't make any difference which supports the theory
|
||||
// that it's Xen who corrupts the byte stream.
|
||||
cfmakeraw(&tio);
|
||||
if (tcsetattr(fd1, TCSANOW, &tio) < 0)
|
||||
{
|
||||
perror("tcsetattr 1");
|
||||
return errno;
|
||||
}
|
||||
if (tcsetattr(fd2, TCSANOW, &tio) < 0)
|
||||
{
|
||||
perror("tcsetattr 2");
|
||||
return errno;
|
||||
}
|
||||
*/
|
||||
if (argc == 4)
|
||||
mark = argv[3][0];
|
||||
|
||||
while (1)
|
||||
{
|
||||
size = read(fd1, &c, 1);
|
||||
if (size <= 0)
|
||||
break;
|
||||
|
||||
parse:
|
||||
if (c == 0x0d)
|
||||
{
|
||||
size = read(fd1, &c, 1);
|
||||
if (size <= 0)
|
||||
{
|
||||
out(0x0d);
|
||||
break;
|
||||
}
|
||||
if (c == 0x0a)
|
||||
{
|
||||
out(0x0a);
|
||||
}
|
||||
else
|
||||
{
|
||||
out(0x0d);
|
||||
goto parse;
|
||||
}
|
||||
}
|
||||
else
|
||||
out(c);
|
||||
}
|
||||
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
> This utility is a unidirectional relay so you need to run two instances to get duplex communication, like:
|
||||
>
|
||||
> #!/bin/sh
|
||||
>
|
||||
> id1=$(xl domid "$1-dm")
|
||||
> id2=$(xl domid "$2-dm")
|
||||
> tty1=$(xenstore-read /local/domain/${id1}/device/console/3/tty)
|
||||
> tty2=$(xenstore-read /local/domain/${id2}/device/console/3/tty)
|
||||
> ./ptycrlf ${tty1} ${tty2} - &
|
||||
> ./ptycrlf ${tty2} ${tty1} + &
|
||||
|
||||
> With this everything should be good:
|
||||
>
|
||||
> ~~~
|
||||
> Opened \\.\com1
|
||||
> Waiting to reconnect...
|
||||
> Connected to Windows 7 7601 x64 target at (Wed Mar 19 20:56:31.371 2014 (UTC + 1:00)), ptr64 TRUE
|
||||
> Kernel Debugger connection established.
|
||||
> Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols
|
||||
> Executable search path is:
|
||||
> Windows 7 Kernel Version 7601 MP (1 procs) Free x64
|
||||
> Built by: 7601.18247.amd64fre.win7sp1_gdr.130828-1532
|
||||
> Machine Name:
|
||||
> Kernel base = 0xfffff800`0261a000 PsLoadedModuleList = 0xfffff800`0285d6d0
|
||||
> System Uptime: not available
|
||||
> ~~~
|
||||
|
||||
Happy debugging!
|
Loading…
Add table
Add a link
Reference in a new issue