mvn site がとても遅い

Surefire レポートの表示に必要な css 類は、 mvn site で生成する必要があるが、 依存ライブラリが多いと激重*1だったので、その解決方法のメモ。

  • mvn -o site で改善しなかった。

悪さをしているプラグインは以下の様子。

maven.apache.org

ドキュメントにある skip パラメータを有効にするために -Dmpir.skip=true を指定した。

mvn site -Dmpir.skip=true
mvn surefire-report:report

*1:しかも認証プロキシ環境なのに、一部ダウンロードがproxy設定を無視するせいでエラーが大量に出る

struct をletでパターンマッチングする

#[allow(dead_code)]
struct St {
   i: i32,
   j: i32,
   k: i32,
}

fn new() -> St {
    St{i:1, j:2, k:3}
}

fn main() {
    let St {
        i,           // フィールド名と同名で束縛
        k: alt_name, // フィールド名と異なる名前に束縛(順序が変わってもOK)
        ..           // いらないフィールドは .. で省略できる
    } = new();
    println!("{} {}", i, alt_name); // 1, 3
}

Result について

Result<T, E> という型が、std::result というモジュールに定義されています。

この型は、ファイルの入出力やオープンのような失敗する可能性のある関数の戻り型として使われます。

宣言は以下のような感じ。

enum Result<T, E> {
    Ok(T),
    Err(E)
}

enumなのでパターンマッチして使用するわけです。

たとえば、ファイルをオープンするのに、以下のようなコードを書きます。

use std::fs::File;


let mut file = match File::open("some.txt") {
    Ok(file) => {file}                           // Okなら、そのまま返す。
    Err(why) => {panic!(why.to_string())}        // 失敗時は、panicでプログラム終了
}

unwrap() : エラー時に panic を呼び出す。

いちいち match して Okの値を取り出すのは面倒なので、Okの値を取り出す unwrap() という関数があります。

let mut file = File::open("some.txt").unwrap();

file には、Ok(x) の x が代入されます。 open が Err(why) を返すと、unwrap() は why の値を表示してpanicします。

"{:?}"でフォーマットして出力するので、あまり見やすい表示とは言えませんが。

thread '<main>' panicked at 'called `Result::unwrap()` on an `Err` value: Error { repr: Os(2) }', /home/rust/src/libcore/result.rs:729

ok().expect(パニック時の表示文字列)

why を捨てていいなら、Result<T,E>::ok() で Option に変換できるので、以下のように書けます。

let mut file = File::open("some.txt").ok().expect("open error");

この場合は、エラー原因である why を捨てているので、ファイルのオープンエラーが発生したことはわかりますが、その理由がわかりません。

try! マクロで早期リターン

try! マクロを使うとエラー時にはResultをリターンして、正常時には Ok の値を返します。

fn read_from(fname: &str) -> io::Result<String> {
    let mut s = String::new();
    let mut file = try!(File::open(fname));
    try!(file.read_to_string(& mut s));
    return Ok(s);
}

fn main() {
    match read_from("some.txt") {
        Ok(s) => {println!("{}", s);
        Err(why) => {println!("{}", whly.to_string())}
    };
}

その他の関数

Result<T, E>には、他にも以下の関数があります。

and(res: Result<U,E>) -> Result<U, E> - Ok 時には、resを返す。Err時には selfを返す。

and_then(処理) - Ok時には、Ok値を引数に処理を実行し、Err時には、Err(why) を返す。

or(res: Result<U, E>) - Ok時には、selfを、Err時には、resを返す。

or_else(処理) - Err 時にはErr値を引数にとる処理を実行し、Ok 時にはResultのまま返す。

unwrap_or(opt:T) - Err 時には opt を返す。

unwrap_or_else(処理) - 返り値が、Resultではなくて、Tになる。

unwrap_err - Errの値を返す。Okの場合は、panicする。

使用例

use std::fs::File;
use std::io;
use std::io::Read;

fn read_from(fname: & str) -> io::Result<String> {
    return File::open(fname).and_then(|mut file| {
        let mut s = String::new();
        //try!(file.read_to_string(& mut s));
        //return Ok(s);
        return file.read_to_string(& mut s).and(Ok(s));
    });
}

fn main() {
    match read_from("unwrap.rs") {
        Ok(s) => {println!("{}", s)}
        Err(why) => {println!("{}", why.to_string())}
    };
}

add_libraryのメモ

メモ代わりの参考訳

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            source1 [source2 ...])

<name> はターゲットの論理名で、プロジェクト内でユニークでなければならない。

  • STATIC は静的ライブラリである。

  • SHARED は実行時に動的にリンクされる。

  • MOULDE は動的にdlopenのような関数を用いてロードされる。プラグインなどで使用される。

ライブラリのタイプを指定しない場合は、 BUILD_SHARED_LIBSON ならば、 SHARED に、 OFFなら STATIC になる。

SHAREDMODULEPOSITION_INDEPENDENT_CODE が自動的に True に設定される。 なお、 STATIC の同設定は CMAKE_POSITION_INDEPENDENT_CODE の設定値で自動的に初期化される。

ビルド結果は、ソースツリーに関連するビルドツリーに出力される。 `LIBRARY_OUTPUT_DIRECTORYRUNTIME_OUTPUT_DIRECTORY の2つのプロパティで出力先を制御できる。

OUTPUT_NAME で出力されるファイル名の <name> の部分を変更できる。

EXCLUDE_FROM_ALL を引数として与えると、ALLターゲットから除外できる。

オブジェクトライブラリ

add_library(<name> OBJECT <source1> [<source2> ...])

以下のように他のターゲットとリンクする。以下では other ターゲットに OBJECT 指定してビルドした objlib をリンクしている。

add_library(other $<TARGET_OBJECTS:objlib>)

ライブラリの別名

add_library(<name> ALIAS <target>)

STLのless

ひさびさにSTL書いたら、コンパイルエラーでげんなりしたのでメモ。

struct A {

    bool operator<(const A &other) const;

    もしくは
    friend bool operator<(const A &lhs, const B &rhs);
};

constメンバ関数にし忘れることが多い。

[tcpdump] libpcap でキャプチャデータを解析する際のメモ

libpcap で、保存されたキャプチャファイルを解析する方法を調べたのでメモ。

おおまかなソース

#include <pcap.h>

const u_char *pkt;
char pcap_errbuf[PCAP_ERRBUF_SIZE];
const char *pcap_path = "...";
pcap_t *pcap = pcap_open_offline_with_tstamp_precision(pcap_path,
        PCAP_TSTAMP_PRECISION_NANO, errbuf);
if (pcap == NULL) {
    fprintf(stderr, "pcap_open: error %s\n", errbuf);
    exit(EXIT_FAILURE);
}

struct pcap_pkthdr pkthdr;
while ((pkt = pcap_next(pcap, &pkthdr))) {
}
pcap_close(pcap);

おおまかな流れ

  1. pcap_open または、その亜種でパケットファイルをオープンする。 パケットファイルのハンドルとして、 pcap_t が割り当てられる。

  2. パケットを処理するには、以下の選択肢がある。

    • pcap_next() でパケットを取り出し、処理する。
    • pcap_offline_read() でコールバックを指定して、libpcap内部でループさせる。user引数で任意のユーザデータを渡せる。

    pcap_next() は内部的に pcap_dispatch() を1度づつ呼び出しているので、pcap_offline_read() のほうが 効率がよさそう。ただ、コールバックベースでプログラム書くのは面倒であれば、 pcap_next() でもOK。

  3. pcap_close() でハンドルを閉じる。

pcap_next を使った場合の説明

pcapファイルのオープン

pcap_t *pcap_open_offline_with_tstamp_precision(...) で pcapファイルをオープンする。 上のサンプルでは、ナノ秒精度のタイムスタンプを保持したキャプチャファイルを読み込むので、 PCAP_TSTAMP_PRECISION_NANO を指定した。

エラー時には、NULL が返り、errbufにエラーメッセージが格納される。

パケットの読み込み

const u_char *pcap_next(pcap_t *, struct pcap_pkthdr *) でパケットを読み取る。

pcap_pkthdr にはパケットのキャプチャ時刻とパケット長が設定され、関数の戻り値にパケットのバイト列へのポインタが格納される。

struct pcap_pkthdr {
    struct timeval ts;
    bpf_u_int32 caplen;
    bpf_u_int32 len;

先頭部分は、Ethernetのフレームなので自力でパースして、IPのヘッダの先頭のポインタを計算する。 具体的な方法はTBW

(VLANなどの対応が面倒だったけど割愛。この辺面倒みてくれるライブラリはきっとあるはず・・・)

あとは、netinet/ip.hnetinet/tcp.hnetinet/udp.h の定義を元に、パースする。

pcapファイルのクローズ

pcap_close(pcap_t*) で pcap ファイルを閉じる。

フィルタの利用

tcpdumpのフィルタ式を利用できる。

フィルタ式のコンパイル

  1. struct bpf_program の変数を宣言する。
  2. pcap_compile(pcap_t *, struct bpf_program*, int, int) を呼び出す。(pcapはpcap_open済みのもの) 第2引数は最適化するかどうか、第3引数はネットマスク(キャプチャファイルの場合は 0 でも問題ないようだが・・・・)

方法1:フィルタ式の設定

pcap_setfiler(pcap_t *, struct bpf_program*) でフィルタを設定する。

後続の pcap_next の呼び出しは、フィルタにマッチするパケットだけを返す。

方法2:フィルタ式の適用

pcap_setfilter を呼び出すかわりに、pcap_next が返したパケットがフィルタ式を満たすかどうかを、 ユーザーコードで確認する。

pcap_offline_filter(struct bpf_program*, struct pcap_pkthdr*, const u_char* pkt) を呼び出す。pktにはpcap_nextの戻り値を指定する。フィルタに適合する場合は、非ゼロの値を返す。

tcpdump での vlan利用時の注意点(IEEE802.1Q)

フィルタ式にport n みたいな指定をしても、VLANタギングされているパケットがマッチしないので、 vlan and (port n) のように指定すればいい。 (tcpdump -d でパケットマッチングコードを見る限り、vlan 指定しない場合は、12-13バイト目がIPv6でもIPv4かどうかだけがチェックされるのでマッチしない)

もし、VLANタグがついているパケットと、ついていないパケットの両方にフィルタ式 expr を適用したい場合は、 "expr or (vlan and expr)" のように指定する。