OpenStackのNeutronでfloating IPを設定する例

内部Networkの192.168.50.100を外部ネットワークにマッピングする例を示します。

まず、内部ネット192.168.50.100のIPをもっているportのPORT_IDを調べます。
# neutron port-list
+--------------------------------------+------+-------------------+---------------------------------------------------------------------------------------+
| id                                   | name | mac_address       | fixed_ips                                                                             |
+--------------------------------------+------+-------------------+---------------------------------------------------------------------------------------+
| 02cd18bc-768b-4b9b-937b-40dfe66fe33e |      | fa:16:3e:41:3f:01 | {"subnet_id": "2cc97205-b498-4e53-8fe5-21dd6f5da317", "ip_address": "192.168.30.128"} |
| 897101af-581f-42da-b8b6-56b8352cf53f |      | fa:16:3e:b2:47:71 | {"subnet_id": "ad914263-519c-40eb-a9b4-421485e5ece3", "ip_address": "192.168.50.101"} |
| ace36996-0315-4a7d-8eaa-379bb67bd688 |      | fa:16:3e:95:40:18 | {"subnet_id": "ad914263-519c-40eb-a9b4-421485e5ece3", "ip_address": "192.168.50.100"} |
| bc538543-d817-48a9-9dd3-9803fe6cf058 |      | fa:16:3e:63:c9:12 | {"subnet_id": "ad914263-519c-40eb-a9b4-421485e5ece3", "ip_address": "192.168.50.1"}   |
+--------------------------------------+------+-------------------+---------------------------------------------------------------------------------------+
次で、以下のように実際にマッピングを作成します。
 # neutron floatingip-create --port-id ace36996-0315-4a7d-8eaa-379bb67bd688 --fixed-ip-address 192.168.50.100 ext-net
Created a new floatingip:
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| fixed_ip_address    | 192.168.50.100                       |
| floating_ip_address | 192.168.30.129                       |
| floating_network_id | 2635ab5a-0784-4898-b91d-dbe1228f31cf |
| id                  | ba6e13f3-0768-4d8f-8cd6-91217ea558aa |
| port_id             | ace36996-0315-4a7d-8eaa-379bb67bd688 |
| router_id           | 612dafe5-c553-4103-a5d2-96e379ed2e50 |
| tenant_id           | 4df9786b5d044f5e8ae1593e105546d4     |
+---------------------+--------------------------------------+

別の方法

なお、次のようにfloating IPを作成して、その後、associateすることもできる。
# neutron floatingip-create ext-net
+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| fixed_ip_address    |                                      |
| floating_ip_address | 192.168.158.82                       |
| floating_network_id | 53fe6eb3-6558-47a5-a758-3b2aa1f102ce |
| id                  | b4cd47df-92e1-433d-b48f-152b79063984 |
| port_id             |                                      |
| router_id           |                                      |
| tenant_id           | c5f8ac76b2a541c09da7c0fa8f23c03f     |
+---------------------+--------------------------------------+
# neutron floatingip-associate b4cd47df-92e1-433d-b48f-152b79063984 ace36996-0315-4a7d-8eaa-379bb67bd688
上記こまんどの第二引数はfloatingipのID、第三引数はport_idを表す。

iptablesでMASQUERADEやDNATを設定する例

IPマスカレード

セキュリティなど細かいことを気にしない環境なら、以下の例のようにシンプルにLinuxマシンをNAT機能付きのルータにできる。
次の例は、192.168.118.0/24からのパケットをマスカレード(パケットのソースIPをこのマシンのように見せかけて転送)する。これは、プライベートアドレスをもつマシンから外部へ通信する場合に役立つことが多い。以下のコマンドを図のRouter machineで実行する。
# iptables -t nat -A POSTROUTING -s 192.168.118.0/24 -j MASQUERADE 


外部からのアクセス

逆に外部から192.168.118.0/24にあるマシンにアクセスしたい場合は、DNATを利用する。 以下はRouter machineの8022ポートへのパケットを192.168.118.10の22ポートに転送する例。
# iptables -t nat -A PREROUTING -p tcp --dport 8022 -j DNAT --to-destination 192.168.118.10:22


注意事項

ただし、上記の他にも、そもそもIPのルーティングが許可されていなければならない。 必要に応じて、下記の設定も行う。
# echo 1 > /proc/sys/net/ipv4/ip_forward

C言語入門2 (文字入力)

今回は、キーボードから文字を入力してみましょう。 次のmy_prog2.cというファイルを用意します。
#include <stdio.h>
#include <stdlib.h>

#define MAX_NAME_LENGTH 100

int main(void)
{
        char name[MAX_NAME_LENGTH];
        printf("What are your name ? : ");
        fgets(name, MAX_NAME_LENGTH, stdin);
        printf("Hello, %s\n", name);
        return EXIT_SUCCESS;
}
これを前回と同じく次のようにビルドします。
$ gcc my_prog2.c -o my_prog2
さて、実行させると次のようになります。
$ ./my_prog2 
What are your name ? :
ここで、Lionと入力してみましょう。
$ ./my_prog2 
What are your name ? : Lion  (キーボードで入力する)
Hello, Lion

そうすると、Hello, [入力した名前]が表示されました。

解説

今回、新しく下記の4つの事柄が出てきました。それぞれ説明していきます。
  • #define文
  • 文字を格納する変数(name)
  • fgets関数
  • printfの中の%s

#define文

#defineの後には、通常、2つの語が並びます。この文はこれ以降に出現する1つ目の単語を2つ目の単語に置換します。 今回の例ですと、MAX_NAME_LENGTHが100に置換されるので、name[MAX_NAME_LENGTH]はname[100]と書いた場合と同じになります。
では、なぜ、わざわざこのような面倒なことをするのでしょうか?それには、主に2つ理由があります。 1つは、複数回使用される数値をこのように定義しておくと、数値の変更が必要なとき、#define行の値を変えるだけで対応できること。 もう1つは、その数値が何を表しているのかが分かりやすくなるためです。

文字を格納する変数(name)

キーボードから文字列を入力するには、それを格納するための場所が必要になります。C言語では、文字列や数字などを格納するものを変数と呼びます。 変数は、使用する前に、その種類と名前を宣言しなければなりません。 このプログラムの場合、種類は文字列(char)で名前はnameです。 さらに、文字列の場合、格納できる最大文字数も、次の形式で一緒に指定する必要があります。
    char 変数名[最大の文字数];

fgets関数

fgets()関数は、キーボードから文字列を受付け、変数に格納します。括弧の中にコンマで区切られた3つの単語があります。 それぞれの語を引数(ひきすう)といいます。左側から第一引数、第二引数、第三引数とも呼ばれます。fgets()の第一引数には、入力を格納する文字列用の変数を指定します。 第二引数には、その変数に格納できる最大の文字数を指定します。第三引数にはstdinという語を指定しています。これは、キーボードから入力することを意味します。

printf関数の中の%s

printf()の第一引数の文字列の中に%sがあり、第二引数に文字列の変数が指定されている場合、%sがその変数の内容に置換されて画面に出力されます。 なので、このサンプルでは入力した文字列が、そのまま%sの箇所に出力されています。

次回

C言語入門3 (数値型変数と演算)

Open vSwitchコマンドメモ

下記でイタリックの部分は適宜、書き換える。PORT_NAMEには、ifconfigで表示されるinterface名を入れる。

構成を表示
# ovs-vsctl show

Portを追加
# ovs-vsctl BRIDGE_NAME PORT_NAME
Portを追加 (VLAN tagを指定)
# ovs-vsctl BRIDGE_NAME PORT_NAME tag=TAG_ID

Portを削除
# ovs-vsctl del-port PORT_NAME

Flowを表示
# ovs-ofctl show SWITCH_NAME

Openstack (neutron)がつくるNetwork Interfaceの関係

仮想マシン

OpenStackでneutronとopenvswitchを使った構成では、仮想マシン毎に 以下のような4つのインターフェイスが作成される。最初の3文字まはた4文字が異なり後は同じ数値の組です。
なお、これは、nova.confに以下の設定がある場合です。
libvirt_vif_driver=nova.virt.libvirt.vif.LibvirtHybridOVSBridgeDriver
  • qbr655843be-3b
  • qvb655843be-3b
  • qvo655843be-3b
  • tap655843be-3b
これらは次のように接続されている。

tapから始まるI/FはQEMU (KVM)などが作るゲストOSに接続されている。それがqbrから始まるBridgeに接続されている。 Bridgeの構成は、次のようにbrctrlコマンドで確認できる。
# brctl show
bridge name              bridge id                           STP enabled     interfaces
qbr655843be-3b      8000.f258a8426856       no                      qvb655843be-3b
                                                                                                         tap655843be-3b
qvbとqvoは、virtual ethernet tunnel (veth)の両端のデバイスである。qvbはBridge側に、qvoはOpen vSwitch側に接続されている。

DHCP agent

DHCP agentのネットワーク構成を下図に示す。

テナントネットワークごとのDHCPサーバは、独自のnetwork namespaceで実行されている。それは、virtual ethernet tunnelを経由してグローバルなnetowrk namespaceでOpen vSwitchに接続される。
なお、network namespaceの一覧を表示させるには次のようにする
# ip netns
qdhcp-26ac80d0-9f3d-46bf-9c04-27ecdcbd7800
qrouter-f53f573b-5c5f-4136-af7b-c7879cde9ed3
qrouter-d1982370-acba-4861-b706-185ccfd52cce
qdhcp-6e1aec2f-ad9c-4aec-b49e-cc5a85a1f1e5
また、次のようにnetwork namespaceを指定して、コマンド実行することで、その空間でのinterface一覧を取得できる。
# ip netns exec qdhcp-26ac80d0-9f3d-46bf-9c04-27ecdcbd7800 ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

ns-9e1da44f-f2 Link encap:Ethernet  HWaddr FA:16:3E:AA:A4:B2
          inet addr:10.5.20.3  Bcast:10.5.20.255  Mask:255.255.255.0
          inet6 addr: fe80::f816:3eff:feaa:a4b2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:468 (468.0 b)  TX bytes:468 (468.0 b)

C言語入門1 (文字列表示)

C言語を始める方を対象に解説記事を書きます。環境はLinuxを想定していますが、MacOSのターミナルでもほぼ同様のことができると思います。

 C言語で最小のプログラムを書いて実行する流れは次のとおりです。
  • エディタでプログラムを記述する
  • コンパイルする
  • 実行する 
エディタは、プログラムを入力/編集するためのツールです。Linuxだとvi系(vimなど)とEmacs系を使っている人が多いと思います。コンパイルとは、テキスト(人が書いた、文字として見えるプログラム)を実行可能な形式(バイナリとも呼ばれます)に変換することを意味します。

バイナリ形式は、(コンピュータにとって意味のある)数値の羅列ですが、人間にとっては非常に内容の認識が困難です。コンパイルされる前のテキストのプログラムをソースコード(または、ソース)ということもあります。ソースは、英語の起源を意味するsourceをそのまま日本語として使用しています。

エディタでプログラムを記述

次のソースコードを作成し、my_first.cという名前で保存してください
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
        printf("Hello, World!\n");
        return EXIT_SUCCESS;
}

コンパイル

次のコマンドを実行してください。
$ gcc my_prog1.c -o my_prog1
エラーが表示されなければOKです。

実行

次のように実行してみてください。
$ ./my_prog1 
Hello, World!

解説

ソースコードの内容を見ていきましょう。 冒頭の2行に#includeから始まる文があります。 プログラムの中で使用する機能によって、このような#include文を適宜記載します。 このプログラムの場合、stdio.hの行は、後述するprintf()という機能を使用する際に必要で、 stdlib.hの行は、EXIT_SUCCESSという値を使う際に必要です。

次にint main(void)という行と中括弧{}で囲まれた範囲(だいぶ後で説明しますが関数と呼ばれます)があります。C言語では、このmain関数から実行が開始されます。逆に言えば、main関数がなければ、コンパイルがエラーになります。

関数の最初にprintf()という文があります。これは、文字列を画面に出力する命令です。この中に"Hello, World!\n"と書いたので、それが画面に出力されました。最後の\nは、「改行」を行う特殊な文字列です。また、C言語では、(#からはじまる行と関数の宣言を除いて)文の最後には; (セミコロン)が必要です。

最後のreturn EXIT_SUCCESS文は、このプログラムが正常に終了したことをシステムに通知するための行です。とりえあずは、決まり文句と考えておいてもよいでしょう。


次回

C言語入門2 (キー入力)

GDBの出力をフィルタする

GDBのPython機能を利用して、大量の出力から目的の行を抽出する方法を2つ紹介します。

1.reモジュールの利用(Pythonコードによる抽出)

1つ目は、backtraceの出力をPythonのreモジュールを使って目的の行を探します。主に青字の部分を目的に応じて変更してください。
(gdb) py out=gdb.execute("i proc map", to_string=True); import re; [re.search("Named", line) and print(line) for line in out.split("\n")];

2.シェルを利用

2つ目は、コマンドラインのgrepを利用しています。単純な検索だとPythonのプログラム(それもワンライナー)を組むより容易に目的を達成できる場合もあるでしょう。
(gdb) py a=gdb.execute("bt",to_string=True); import subprocess; subprocess.Popen("echo '%s' | grep main" % a, shell=True).wait()
なお、上記のいずれも技術的には、Pythonをワンライナを使用しています。 繰り返しはリスト包含(list comprehension)、分岐(if)にはand/orの遅延評価を利用しています。 関数はlamdaで変数にしています。

使用例

通常、i proc mapコマンドを実行すると多くの行が出力されます。catコマンドを使って試してみましょう。
$ gdb -q /bin/cat   
Reading symbols from /bin/cat...(no debugging symbols found)...done.
(gdb) run
Starting program: /bin/cat 
^C <= 実行を中断するためにCTRL + Cを入力
Program received signal SIGINT, Interrupt.
0x00007ffff7afe6a0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81      ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) i proc maps
Too many parameters: maps
(gdb) i proc map 
process 19540
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x40b000     0xb000        0x0 /bin/cat
            0x60a000           0x60b000     0x1000     0xa000 /bin/cat
            0x60b000           0x60c000     0x1000     0xb000 /bin/cat
            0x60c000           0x62d000    0x21000        0x0 [heap]
      0x7ffff7a12000     0x7ffff7bcf000   0x1bd000        0x0 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7bcf000     0x7ffff7dcf000   0x200000   0x1bd000 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7dcf000     0x7ffff7dd3000     0x4000   0x1bd000 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7dd3000     0x7ffff7dd5000     0x2000   0x1c1000 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7dd5000     0x7ffff7dda000     0x5000        0x0 
      0x7ffff7dda000     0x7ffff7dfd000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.17.so
      0x7ffff7fcc000     0x7ffff7fcf000     0x3000        0x0 
      0x7ffff7ff8000     0x7ffff7ffa000     0x2000        0x0 
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x22000 /lib/x86_64-linux-gnu/ld-2.17.so
      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x23000 /lib/x86_64-linux-gnu/ld-2.17.so
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
上記では、マップされているライブラリの数がそれほど多くないので、目視で、目的のファイルを探すことも、それほど困難ではありませんが、firefoxやchromeのような大規模なプログラムでは数百行以上表示されます。 これを、stackの入っている行のみを表示させるには次のように入力します。かなりシンプルになることが理解いただけると思います。
(gdb) py out=gdb.execute("i proc map", to_string=True); import re; [re.search("stack", line) and print(line) for line in out.split("\n")];
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]

参考

GDBからPython使用法については下記にも述べられています。 http://transparent-to-radiation.blogspot.jp/2014/01/gdbpython-script.html

KVMをIntel CPUでネストして実行する


KVMをIntelプロセッサでネストして実行する設定
  • カーネル3.2以上を用意する
  • /etc/modprobe.d/kvm-nested.confに以下を設定する
  • options kvm_intel nested=1
  • 再起動する
なお、再起動後、次のように、Yが表示されれば成功
# cat /sys/module/kvm_intel/parameters/nested 
Y

neutronコマンドがAuthentication requiredを返す際の対処法

neutronがAuthentication requiredを返してかつ、/var/log/neutron/server.logに次のような記録がある場合、古い認証キーが使われている可能性があります。
2014-01-26 16:16:15.038 2814 WARNING keystoneclient.middleware.auth_token [-] Verify error: Command 'openssl' returned non-zero exit status 4
2014-01-26 16:16:15.039 2814 WARNING keystoneclient.middleware.auth_token [-] Authorization failed for token 7cd65acbdf1048d97f6629c2614eb9f8
このような事は、OpenStackコンポーネントのアップデートなどを繰り返しているうちに発生することがあります。 対処法は、/var/lib/neutron/keystone-signingを削除(あるいは別名に)します。

tcpdumpの使い方

interfaceを指定する

-iオプションを指定する

# tcpdump -i eth0

port番号を指定する

'port ポート番号'を指定する

# tcpdump -i eth0 port 80

パケットの内容をダンプする

-Xオプションを指定する

# tcpdump -i lo port 35357 -X
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes
13:47:55.102252 IP ika.52727 > ika.35357: Flags [S], seq 2674919811, win 32792, options [mss 16396,sackOK,TS val 27498856 ecr 0,nop,wscale 7], length 0
        0x0000:  4500 003c 90eb 4000 4006 286c c0a8 000a  E..<..@.@.(l....
        0x0010:  c0a8 000a cdf7 8a1d 9f70 0983 0000 0000  .........p......
        0x0020:  a002 8018 7016 0000 0204 400c 0402 080a  ....p.....@.....
        0x0030:  01a3 9968 0000 0000 0103 0307            ...h........

出力をパイプでgrepへ渡したり、ファイルへリダイレクトする場合

-lオプションをつけます。これがない場合、一定サイズのデータがtcpdumpから出力されるまで、grepやファイルへデータが送られなくなり、実際にデータが来るまで時間差が生じます。

# tcpdump -i eth0 port 80 -l | grep GET

OpenstackのHorizonでSomething went wrong!と表示される時の対処法

下記のような画面が表示される場合、アクセスしたホストが許可されてない場合があります。

アクセスが許可されていない場合、/var/log/horizon/horizon.logに次のようなログが出ています。
2014-01-25 23:13:34,980 21326 ERROR django.request Internal Server Error: /dashboard/
Traceback (most recent call last):
  File "/usr/lib/python2.6/site-packages/django/core/handlers/base.py", line 89, in get_response
    response = middleware_method(request)
  File "/usr/lib/python2.6/site-packages/django/middleware/common.py", line 55, in process_request
    host = request.get_host()
  File "/usr/lib/python2.6/site-packages/django/http/__init__.py", line 223, in get_host
    "Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): %s" % host)
SuspiciousOperation: Invalid HTTP_HOST header (you may need to set ALLOWED_HOSTS): 192.168.158.20
上記に該当する場合、/etc/openstack-dashboard/local_settingsのALLOWED_HOSTS行に上記のログで表示されている最後の部分(青色で強調)を追加するとうまくいきます。
例)192.168.158.20からのアクセスを許可する場合、青字の部分を追加
ALLOWED_HOSTS = ['192.168.158.20', '192.168.0.10', 'ika.localdomain', 'localhost', ]
最後に、httpd (apache)を再起動します。 RedHat系ならコマンドラインで、
# service httpd restart
なお、これはHorizonというよりDjangoのアクセス制御機構に起因する問題です。

Fedora 20をCUIで使う

FedoraはデフォルトではGUIで使うように構成されているが、CentOSのようにXがデフォルトで起動しないようにする。 以下のように/etc/systemd/system/default.targetのリンク先を変更し、再起動すればよい。 (元々はgraphical.targetにリンクされているはず)
# ln -fs /lib/systemd/system/multi-user.target /etc/systemd/system/default.target 
正しく設定できているかは、次のようにして確認できます。
# ls -l /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 37  1月 24 20:29 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target

Fedora 20でNICの名前をeth0にする。

カーネルオプションにnet.ifnames=0を追加すればよい。

具体的には、/etc/default/grubファイルを編集する。GRUB_CMDLINE_LINE行に上記のオプション追加する。

例: 赤字が追加部分
# grep GRUB_CMDLINE_LINUX /etc/default/grub 
GRUB_CMDLINE_LINUX="rd.lvm.lv=fedora/swap vconsole.font=latarcyrheb-sun16 rd.lvm.lv=fedora/root $([ -x /usr/sbin/rhcrashkernel-param ] && /usr/sbin/rhcrashkernel-param || :) rhgb quiet net.ifnames=0"

次に以下のコマンドで設定を有効にする。
grub2-mkconfig -o /boot/grub2/grub.cfg

最後にリブートすれば完了

VMware Fusion 6のデフォルトのDHCP範囲

VMware Fusion 6のDHCP範囲は192.168.158.128〜192.168.158.254のようだ。

設定は、以下のファイルにあります。
/Library/Preferences/VMware Fusion/vmnet8/dhcpd.conf

 次のようにrange行をみれば分かる。
$ grep range /Library/Preferences/VMware\ Fusion/vmnet8/dhcpd.conf 
        range 192.168.158.128 192.168.158.254;

GDBでアタッチ中のプロセスのメモリマップを表示

アタッチしているプログラムのメモリマップをinfo proc mapで表示できます。以下では、catコマンド使った例を示します。

引数なしで実行されたcatは、起動したまま標準入力を待ち続けます。その状態で一旦GDBからコマンドを実行できるようにするため、 途中で、CTRL + Cを入力しています。

$ gdb /bin/cat
[略]
(gdb) run
Starting program: /bin/cat 
^C <= CTRL + Cを押して実行を中断します。
Program received signal SIGINT, Interrupt.
0x00007ffff7afe6a0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81      ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) info proc map
process 31692
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x40b000     0xb000        0x0 /bin/cat
            0x60a000           0x60b000     0x1000     0xa000 /bin/cat
            0x60b000           0x60c000     0x1000     0xb000 /bin/cat
            0x60c000           0x62d000    0x21000        0x0 [heap]
      0x7ffff7a12000     0x7ffff7bcf000   0x1bd000        0x0 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7bcf000     0x7ffff7dcf000   0x200000   0x1bd000 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7dcf000     0x7ffff7dd3000     0x4000   0x1bd000 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7dd3000     0x7ffff7dd5000     0x2000   0x1c1000 /lib/x86_64-linux-gnu/libc-2.17.so
      0x7ffff7dd5000     0x7ffff7dda000     0x5000        0x0 
      0x7ffff7dda000     0x7ffff7dfd000    0x23000        0x0 /lib/x86_64-linux-gnu/ld-2.17.so
      0x7ffff7fcc000     0x7ffff7fcf000     0x3000        0x0 
      0x7ffff7ff8000     0x7ffff7ffa000     0x2000        0x0 
      0x7ffff7ffa000     0x7ffff7ffc000     0x2000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x22000 /lib/x86_64-linux-gnu/ld-2.17.so
      0x7ffff7ffd000     0x7ffff7fff000     0x2000    0x23000 /lib/x86_64-linux-gnu/ld-2.17.so
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
また、類似のコマンドにinfo sharedがあります。こちらは、マップされている共有ライブラリのみを列挙します。Syms Readという列は、デバッグ用のシンボル情報が読み込まれているか否かを示します。
(gdb) info shared
From                To                  Syms Read   Shared Object Library
0x00007ffff7ddaaa0  0x00007ffff7df5439  Yes         /lib64/ld-linux-x86-64.so.2
0x00007ffff7a313c0  0x00007ffff7b7c39f  Yes         /lib/x86_64-linux-gnu/libc.so.6

GDBでPython scriptを実行する

最近(少なくともversion 7以降)のGDBは、Python scriptを実行できます。実行には、従来のGDBマクロと同じくsourceコマンドを使用します。

早速、試してみましょう。次のようなPython scriptを用意します。
$ cat hello.py
#!/usr/bin/env python
import sys
sys.stdout.write('Hello, my first script.\n')
GDBを起動して、実行します。
(gdb) source hello.py
Hello, my first script.

Python scriptからのGDB機能の利用

次に、Python scriptからGDBの機能を使用する例を紹介します。gdb.execute()関数を使用すれば、GDBのコマンドラインで行う操作を実行できます。

サンプルのデバッグ対象として以下のtarget.ccを使用します。
[target.cc]
#include <cstdio>
#include <cstdlib>
#include <time.h>

int getval(int div)
{
        return time(NULL) / div;
}

int main(void)
{
        printf("val: %d\n", getval(5));
        return EXIT_SUCCESS;
}
これをshow-bt.pyで次の4つのことを行います。
  • break pointをgetval()関数の先頭に設定
  • プログラムを実行
  • break pointで停止時のbacktraceを表示する
  • break pointで停止時のraxレジスタを表示する
[show-bt.py] 
gdb.execute('b getval')
gdb.execute('run')
gdb.execute('bt')
gdb.execute('p/x $rax')
以下はコマンドラインからスクリプトを実行させる例です。 コマンドライン引数の-qは、GDB起動時のcopyrightなどの表示を抑制します。-xで実行するスクリプトファイルを指定します。
$ gdb -q -x show-bt.py  target
Reading symbols from /home/foo/target...done.
Breakpoint 1 at 0x400627: file target.cc, line 7.

Breakpoint 1, getval (div=5) at target.cc:7
7               return time(NULL) / div;
#0  getval (div=5) at target.cc:7
#1  0x0000000000400656 in main () at target.cc:12
#2  0x00007ffff72d2ead in __libc_start_main (main=, argc=, ubp_av=, init=, fini=, rtld_fini=, stack_end=0x7fffffffe128) at libc-start.c:228
#3  0x0000000000400539 in _start ()
$1 = 0x7ffff763aee8

コマンドの出力をPythonの文字列として取得する

上記の例では、通常のGDBコマンドを実行するだけでしたが、この出力をPythonの文字列として取得してみましょう。これは、gdb.execute()関数のto_string引数にTrueを渡すことで実現できます。Pythonは、文字列処理も得意ですから、容易に特定の文字列を抽出したり、整形して表示することが可能になります。

以下の例では、stacktrace出力の各行からframe番号と最後の単語を抽出して表示します。
$ cat show-bt-and-format.py 
gdb.execute('b getval')
gdb.execute('run')
bt = gdb.execute('bt', to_string=True)
for line in bt.split('\n'):
  words = line.split(' ')
  print '%s %s' % (words[0], words[-1])
$ gdb -q -x show-bt-and-format.py  target
Reading symbols from /home/foo/target...done.
Breakpoint 1 at 0x400627: file target.cc, line 7.

Breakpoint 1, getval (div=5) at target.cc:7
7               return time(NULL) / div;
#0 target.cc:7
#1 target.cc:12
#2 libc-start.c:228
#3 ()