はじめに

QEMUは動的バイナリ変換を用いた完全仮想化式のハイパーバイザである.
QEMUは実行対象の命令列を逆アセンブルし,いちど中間表現に変換したうえで,ホストのアーキテクチャの命令列に変換して実行する.これによりQEMUは異なるアーキテクチャのバイナリを実行することができる.
しかしQEMUは遅かったため,アクセラレータとしてkqemuが開発された.
kqemuはユーザーモードのコードをホストのCPUに実行させる準仮想化ドライバであり,やがてKVMへと変貌を遂げた.さらにKVMはハードウェアによる仮想化支援機能を用いることで,さらなる高速化を実現した.もはやKVMにおいてQEMUはハードウェアのエミュレーションにしか用いられていない.
かわいそうなQEMU!
しかしQEMUの活躍する場面はいまだ多く残されている.たとえばマルウェア解析で.
それでも遅いのは困りものだ.QEMUはどこが遅いのだろうか.
ここではperfを用いてQEMUのボトルネックを雑に特定する.

実験

今回はARMエミュレーションについて調査した.ホスト・ゲストともOSにはUbuntu 12.04(3.2.0-23-generic)を用いた.perfでQEMUのプロファイリングを行っている間,ゲストではgccを用いてコンパイルを実行した.

  • ARM版Ubuntu 12.04のインストール

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # sudo apt-get install qemu
    # wget http://odroid.us/odroid/users/osterluk/qemu-example/qemu-example.tgz
    # tar xzf qemu-example.tgz ./zImage
    # wget http://releases.linaro.org/12.04/ubuntu/precise-images/developer/linaro-precise-developer-20120426-86.tar.gz
    # tar xzf linaro-precise-developer-20120426-86.tar.gz
    # qemu-img create -f raw rootfs.img 3G
    # mkfs.ext3 rootfs.img
    # mkdir mnt
    # sudo mount -o loop rootfs.img mnt
    # rsync -a binary/boot/filesystem.dir/ mnt/
    # sudo umount mnt
  • QEMUのビルド

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # sudo apt-get build-dep qemu
    # git clone git://git.qemu-project.org/qemu.git
    # cd qemu
    # git log | grep '^commit' | head -1 | awk '{print $2}'
    6169b60285fe1ff730d840a49527e721bfb30899
    # git submodule update --init dtc
    # git submodule update --init pixman
    # ./configure --extra-ldflags=-pg --target-list=arm-softmmu
    # make
  • perfのインストール

    1
    2
    # sudo apt-get install linux-tools
    # sudo echo 0 > /proc/sys/kernel/perf_event_paranoid
  • perfによるプロファイリング

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # perf record -a -g -F100000 qemu/arm-softmmu/qemu-system-arm -M vexpress-a9 -m 512 -kernel zImage -sd rootfs.img -append "root=/dev/mmcblk0 rw physmap.enabled=0 console=ttyAMA0" -monitor stdio
    WARNING: Image format was not specified for '../../rootfs.img' and probing guessed raw.
    Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
    Specify the 'raw' format explicitly to remove the restrictions.
    QEMU 2.3.90 monitor - type 'help' for more information
    (qemu) audio: Could not init `oss' audio driver
    (qemu) q
    [ perf record: Woken up 37 times to write data ]
    [ perf record: Captured and wrote 9.465 MB perf.data (~413514 samples) ]
    Failed to open /tmp/perf-6993.map, continuing without symbols
    Failed to open [vmxnet], continuing without symbols
    Failed to open [vmci], continuing without symbols
    Failed to open [vsock], continuing without symbols
    no symbols found in /bin/dash, maybe install a debug package?
    no symbols found in /usr/bin/xargs, maybe install a debug package?
    no symbols found in /usr/bin/updatedb.mlocate, maybe install a debug package?
    no symbols found in /usr/bin/dpkg-query, maybe install a debug package?
  • Flame Graphsによる可視化

    1
    2
    3
    4
    # wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/stackcollapse-perf.pl
    # wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/flamegraph.pl
    # perf script> perf_data.txt
    # perl stackcollapse-perf.pl perf_data.txt|perl flamegraph.pl --title "Flame Graphs - qemu-system-arm" > flamegraphs-qemu-system-arm.svg

perfの結果をFlame Graphsを用いて可視化すると下図のようになった.Flame GraphsはUSENIX LISA’2013にて発表された可視化ツールである.

x軸は時系列,y軸はコールスタックを意味する.横幅が広く上位に位置する関数がボトルネックであるといえる.
よってボトルネックはclear_page()すなわちTCGの各関数で発生しているページフォルトである.

おわりに

ページフォルトだけは勘弁してほしい.

参考文献