Linux」カテゴリーアーカイブ

SoftEther VPN を v4.21 へアップデート

SoftEther VPN を使って私用の VPN 環境を構築して使っている。拠点間を接続するのにとても重宝している。

公開されたからだいぶ経ってしまってはいるが、今回 v4.21 へとアップデートした。
このサイトを参考にして実施したところ無事完了。
設定がちゃんと引き継がれるかが心配だったけど、特に問題はなさそう。

SystemTap を使ったサンプリングに依らない動的なコールグラフの作成

サンプリングに依る動的なコールグラフを作成する方法はいろいろあるけど、これだとサンプルが取得できないパスに関してはコールグラフが取得できないことになってしまう。ソースコード読みをしているときなど完全なコールグラフがほしい時もあって、その方法を考えた。

SystemTap を使うと、すべての関数呼び出しのトレースを取得できる。これを解析すれば完全な動的コールグラフが作成できる。ところが、トレースからコールグラフを作るいいツールがない。

一応、小さいトレースなら、 vonnyfly/kernel_visualization が使えるけど、内部の作り的にトレースをいったんメモリにため込むので、大きなトレースを食わせるとメモリを食いつぶして死んでしまう。用途に合ったものがなければ作るしかないということで、大きなトレースでも扱えるプログラムを C++ で書いた。本来は SystemTap 内でコールグラフを作るのがスマートなんだけど、いまいち stp ファイルの作り方勉強してないからなぁ・・・というところ。

ちなみに SystemTap のトレースは SPEC2006 bzip2 の test 入力で 4GByte 超える。でかすぎ。

C言語のコールグラフ作成のメモ

C言語で書かれたプログラムのコールグラフを作成する方法についてのメモ。

コールグラフとは

Wikipediaのコールグラフの項から定義を拝借。

コールグラフ (マルチグラフとも呼ばれる) とは、コンピュータプログラムサブルーチン同士の呼び出し関係を表現した有向グラフである。

プログラムの解析をしていると、関数の呼び出し関係を視覚的に見たい時がある。そんな時には迷わずコールグラフを描いてみるといい。

静的・動的なコールグラフ

コールグラフには、静的なコールグラフと動的なコールグラフの2通りがある。静的なコールグラフとは、プログラムの実行を伴わずに作成するもの。静的なコールグラフはソースコードのみを解析することで得たり、プログラムをコンパイルする時の情報を使って作成する。コールグラフに存在する関数呼び出しがプログラムを実行したときに起こるかどうかはわからない。

一方、動的なコールグラフは、実際にプログラムを実行したときに起きた関数呼び出しに基づいて作成される。そのため、作成にはプロファイラを用いる。動的なコールグラフにおける関数呼び出しは実際に発生したものであるが、入力に依存する。

解析するときに知りたい情報によってどちらのコールグラフを作るのが望ましいか判断する必要がある。網羅的に関数呼び出しを表現したければ静的なコールグラフを作成するべきだし、とある条件下(入力・環境)におけるプログラムの動作を解析したければ動的なコールグラフを作成するべきだ。また、静的なコールグラフでは大規模になりすぎてみるべきポイントを絞れないといった場合に動的なコールグラフから見て実際に動いてる部分から解析するというアプローチもありだろう。

静的なコールグラフの作成

egyptcodeviz というツールが有名どころなようだ。今回は egypt を用いた。使い方は少し調べれば出てくるから、ほかのサイトを見てもらうとして、自分が使った時に調べた点をいくつかメモしておく。

  • egypt をインストールするときに prefix を変える方法が謎。
  • gcc には -fdump-rtl-expand オプションをつける。
  • 複数のファイルからなるプログラムの場合、.extend ファイルを cat して egypt に与えれば良い。(cat しなくても *.extend みたいな感じでもいいのかもしれないが試していない。)
  • gcc が最適化により関数をインライン化するとコールグラフの結果が変わる。例えば、実際には呼び出されている関数が一度も呼び出されないかのような結果が出力される。ソースコードに忠実なコールグラフを得たければ最適化を無効化するべき。
  • 横向きで出力した方が見やすい気がする。dot で A4 横向きで出力するためには、以下のように指定する。
    -Gsize=11.7,8.3 -Grankdir=LR
  • dot で ps ファイルを出力して、ps2pdf で pdf 化しようとすると、横向きがうまく出力されない。おとなしく dot で -Tpdf を指定して初めから pdf で出力すべき。

動的なコールグラフの作成

こちらも複数ツールがあるようだが今回は gperf2dot.py を利用した。名前に gperf とついているが、gperf だけでなく他のプロファイラの出力も読み取ってくれる。こちらも基本的な使い方はほかのサイトを見てもらうとして、ポイントをメモしておく。

  • gperf2dot.py は縦書き表示に適したように作られているっぽい。A4 縦で出力するためには、dot に以下のように指定する。
    -Gsize=8.3,11.7

【解決済】HP Microserver gen7 で CentOS 7.2 にアップデートすると kernel panic する問題

AMD Neo シリーズの CPU を搭載したマシンを CentOS 7.2 にアップデートすると、起動直後にハングする問題が起きています。自分は N54L を搭載したマシンで発生。

問題のカーネルバージョンは kernel-3.10.0-327.el7 で、boot option を追加すると回避できるとのこと。

Adding initcall_blacklist=clocksource_done_booting to GRUB_CMDLINE_LINUX in /etc/default/grub and then grub2-mkconfig -o /etc/grub2.cfg fix this bug. Thank you for sharing this solution.

参考

Ubuntu 14.04 で SystemTap 2.9 を使う

Ubuntu 14.04 で DTRACE に近い機能を実現する SystemTap を使うための方法。

いくつか参考になる情報があるけど、どうもうまくいかない。

Ubuntu 14.04 (trusty) のリポジトリの SystemTap のバージョンは 2.3 で、これが古くてアップデートされたカーネル(自分の場合は 3.16.0-50)に対応していないのではないかと予想。SystemTap 公式を参考にして最新版をインストールしたところ、使えるようになった。

  1. 必要なパッケージのインストール
    $ sudo apt-get install gcc g++ elfutils libdw-dev
  2. カーネルの dbgsym のインストール
    • ddebs リポジトリの追加
      $ codename=$(lsb_release -c | awk '{print $2}')
      $ sudo tee /etc/apt/sources.list.d/ddebs.list << EOF
      deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse
      deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse
      deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse
      deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse
      EOF
      $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01
      $ sudo apt-get update
    • カーネルの dbgsym パッケージのインストール
      $ sudo apt-get install linux-image-$(uname -r)-dbgsym
  3. SystemTap の入手・インストール
    • 公式の情報
    • リリース版置き場
    • SystemTap の入手
      $ wget https://sourceware.org/systemtap/ftp/releases/systemtap-2.9.tar.gz
    • SystemTap のコンパイル
      $ tar xvfz systemtap-2.9.tar.gz
      $ cd systemtap-2.9
      $ ./configure
      $ make
    • SystemTap のインストール
      $ sudo make install
  4. SystemTap の動作確認
    以下のようになれば OK

    $ sudo stap -e 'probe begin { println("Hello, World!") exit() }'
    Hello, World!

Top で特定のユーザのプロセスを非表示にする

top でプロセスの一覧を眺めている時に root のプロセスが邪魔な時がある。そんな時には以下の方法で特定のユーザのプロセスを非表示にできる。

$ top -U !username

top コマンド起動後に u を押して !username と入力することでも可能。

参考:How to exclude some users from Linux Top screen?

/var/spool/mail/ に届いたメールを処理する方法

概要

cron で処理した結果がメールで送られてくるときに /var/spool/mail/username に届く。mbox 形式で書き込まれて、これは mailx で読んだり消したりできる。だけど、mailx は対話式のプログラムで自動で中身を見て処理したりできない。

メールの数が大量になると(例えば毎分スクリプトを回してたりすると)チェック不能になるし、かといってメールを送らないようにするのはちょっと・・・みたいな時に使えます。

procmail とかで受信時に処理するという手もあると思うけど、今回はまとめてバッチ処理する方法を探してみた。

動作環境は CentOS 7 + Python 2.7。

大雑把な手順

  1. GNU Mailutils の movemail で /var/spool/mail/username を別のディレクトリ(ホームディレクトリ)に移動
    • /var/spool/mail/ はユーザ権限で新しいファイルを作れないので、mbox ファイルを編集するときにロックが取れないという問題があるので、予めメールを移動
  2. mbox を解釈できるライブラリ等を使って mail を解釈して処理
    • 使えるツールはいろいろあると思うが今回は python を使用
    • cron から送られてくるメールに対してテストケースを書いて特定の条件(エラー等)を満たすメールだけを残す、とか特定のファイルに書き出す、とかの使い方を想定

GNU Mailutils のインストール

CentOS のリポジトリにはなさそうだったので公式サイトからダウンロードしてコンパイル・インストール。configure -> make -> make install のいつもの流れで OK。

メール処理スクリプトの例

  • $MAIL が設定されていることを前提(/var/spool/mail/username とかになっているはず)
  • $HOME/mail/INBOX をメールの移動先とした(予め $HOME/mail を作っておく)
  • GNU Mailutils のツールにはパスを通しておく
  • このままは動かないのでご注意
#!/usr/bin/env python

import os
import subprocess
import mailbox

def chkMsg(msg):
    subject = msg["subject"]
    body = msg.get_payload()

    # メッセージの内容によって返り値を変える
    if 削除:
        return 0
    else:
        return 1

mboxpath = os.environ["HOME"] + "/mail/INBOX"
subprocess.call(["movemail", os.environ["MAIL"], mboxpath])

mbox = mailbox.mbox(mboxpath)
to_remove = []
for key, msg in mbox.iteritems():
    if chkMsg(msg) == 0:
        print "Removing: ", key
        to_remove.append(key)

mbox.lock()
try:
    for key in to_remove:
        mbox.remove(key)
finally:
    mbox.flush()
    mbox.close()

subprocess.call(["mail", "-f", mboxpath])

参考