ばいなり、こんこん、こんぱいる!をC87で出します

タイトルはきつねさんシリーズではないですが、相変わらずLLVMネタで同人誌を出します。 タイトルは「ばいなり、こんこん、こんぱいる!」です。 今回は何かをもじってネタにしようということでこの名前になりました。自分が考えた「ggr!きつねさん」は没になりました。

前回はClangネタでしたが、今回からまたLLVMネタに戻りました。 今まではテーマを決めて二人がそれに沿ってネタを作るというスタイルでやっていましたが、 今回は特にテーマを統一せずに好きなことをやるという感じになっています。 自分は3章担当で、LLVMJIT(OldJIT)とMCJITの違いについてソースコードを追いかけながら簡単に解説するという内容になっています。 4章は餅さん(@sui_moti)が担当で、LLVMGCについて解説しながら実際に実装を行っています。 表紙や挿絵(!)は矢上さん(@re_sonanz, web)にお願いしています。

今回はなかなかやる気がでず筆が乗らず、ページ数が今までよりも少なくなりましたが内容としては十分面白いのではないかなと思ってたりします。 前回200部刷って少しだけあまったのと、今回あまり書けなかったのもあって70部になります。 値段が前回とあまり変わってないのはすみません!!

以下、頒布場所の詳細になります。部数制限等は特にありません。

以下、章構成。

  • はしがき
  • はじめに
    • 本書の構成
    • 本書内の記載について
    • お断り
  • 環境構築
    • 本章の概要と構成
    • 環境
    • インストール
      • ビルドに必要なパッケージのインストール
      • LLVM/Clang のビルド/インストール
      • パスの設定
  • もうJIT してられない
  • ごみ集め
    • 本章の概要
    • GC
      • 概要
      • 色々なGC
    • LLVMGC
      • GC を実装するのに必要なこと
    • ShadowStack を使ってみる
      • ShadowStack
      • ランタイム
      • コード生成
      • 動作確認
    • 独自のGC プラグインを実装する場合
  • あとがき
  • 著者/関係者一覧

Fluentdのin_tailプラグインで複数行のログをよむ方法

Fluentd v0.10.45からin_tailプラグイン複数行のログを読み込めるようになりました。 これは今まで外部プラグイン(fluent-plugin-tail-multiline)として提供されていた機能をマージしたものになります。v0.10.50で複数行ログの読み込みに関するバグ修正が入っているので、この機能を使う場合はv0.10.50以上を使いましょう。

基本的な使い方

基本的にはドキュメントに書かれている通りですが、in_tailのformatオプションにmultilineを指定することで複数行モードになります。実際のパースのためのルールはformatN(Nは1から20)という名前のオプションで記述します。

以下は公式ドキュメントから引用したサンプルでRailsのログのパースする設定となります。

format multiline
format_firstline /^Started/
format1 /Started (?<method>[^ ]+) "(?<path>[^"]+)" for (?<host>[^ ]+) at (?<time>[^ ]+ [^ ]+ [^ ]+)\n/
format2 /Processing by (?<controller>[^\u0023]+)\u0023(?<controller_method>[^ ]+) as (?<format>[^ ]+?)\n/
format3 /(  Parameters: (?<parameters>[^ ]+)\n)?/
format4 /  Rendered (?<template>[^ ]+) within (?<layout>.+) \([\d\.]+ms\)\n/
format5 /Completed (?<code>[^ ]+) [^ ]+ in (?<runtime>[\d\.]+)ms \(Views: (?<view_runtime>[\d\.]+)ms \| ActiveRecord: (?<ar_runtime>[\d\.]+)ms\)/

複数行モードの設定にはいくつか注意しないといけないことがあります。

  • formatNは必ず1から初めて連続していなければいけません。
  • 各formatには必ず1つは名前付きキャプチャが含まれてなければいけません。
  • 同じ数値で複数回書いた場合は後に書かれたルールが有効になり、前のものは単純に無視されます(はまりポイントその1)。
  • 正規表現のマッチにはRegexpRegexp::MULTILINEが使われるため、.(ドット)は改行にもマッチするようになります(はまりポイントその2)。
  • 正規表現に渡される文字列(ログ)は最後の改行が削除されているので完全にマッチさせる場合には最後に改行を含んではいけません(はまりポイントその3)。

サンプルの2行目にあるformat_firstlineオプションは正規表現のマッチを始める行を指定するもので、指定しないこともできます。このオプションについてはもう少し詳しく説明します。

format_firstlineの使い分け

format_firstlineを指定した場合、この正規表現にマッチした行からログをバッファに貯めはじめ、次にformat_firstlineにマッチしたときにそれまでのバッファをformatNでパースしてメッセージを生成します。 逆に指定しなかった場合は、常に各行をバッファに貯めはじめ、formatNに完全にマッチした段階でメッセージを生成します。 どちらもメッセージを生成した段階でバッファをクリアします。

自分はこの仕様を見た時にformat_firstlineは必要ないんじゃないかと思っていたのですが、よく考えると必要な場合もありました。 典型的にはパースしたいログの終端が予測できない場合にformat_firstlineが必要です。

例えば、メッセージの先頭にタイムプスタンプをつけてログに残すけどメッセージに改行が含まれることを許す場合です。

Jun 28 19:45:00 message1 foo!
Jun 28 19:45:05 message2 foo!
bar!
baz!
Jun 28 19:45:10 message3 foo!

上記のようなログの場合は各メッセージの終端が予測できないため、 先頭にタイムスタンプが来るまで というルールになります。 この場合にはformat_firstline^[A-Z][a-z]{2} \d{2} \d{2}:\d{2}:\d{2}とでもいれておけば良いでしょう。

逆にこうなるとformat_firstlineオプションは必須でこれだけで良いでは?という気持ちになりますが、実はそうではなくメリット・デメリットがあります。

format_firstlineのメリット・デメリット

format_firstlineオプションを使った場合は、その仕様上 次のメッセージが来るまで今のメッセージの終端がわからない ということになります。 つまり、メッセージがログに出力されてもすぐにそのログがFluentdのメッセージとして送信されるわけではなく、次のログが出てくるまで遅延することになります。ちなみに、次のログが来る前にFluentdを落としたら「今バッファに溜まっているメッセージはロストしないのか」というのが気になりますが、終了前にformatNの判定が行われるためロストすることはありません。

一方でformat_firstlineを使わない場合はログの終端が予測できるため、formatNにマッチした段階でメッセージを送信が可能になり、遅延することはありません。逆にこちらのデメリットとしては、 間違った正規表現(formatN)を書いても気づけない という問題があります。formatNにマッチするまで「まだログが不完全」と判断されるため、永遠にバッファにたまり続けます。

また、どちらの場合も(複数行として出されるものの)各行が一度にログに出力されない場合は、途中までしかFluentdのin_tailが読み込んでいない可能性があり、終了時のパースで失敗していまうのでそのログはロストします。これは結構注意しなければいけないことで、複数行ログの場合は一度にログを出力できなければロストする可能性があることを念頭に置きましょう。

まとめ

Fluentdのin_tailプラグイン複数行ログの読む場合にはformat_firstlineを使う方法と使わない方法があり、それぞれにメリット・デメリットがあります。 どちらのケースが適しているかはよく考えて選びましょう。ちなみに個人的にはformat_firstlineを使わない方法をよく使っています。

記事に間違いなどがあれば随時修正します。

Ruby FFIを使ったエクステンションの作り方

Ruby FFIについて

ここ最近libclangRuby Bindingのffi-clangに機能を追加していたのですが、その過程でFFI(Foreign function interface)というものを知りました。FFIとはようするに多言語を呼び出すためのインターフェイスのことのようで、RubyにおけるFFIlibffiを利用しているようです。

Rubyでは(C言語で)エクステンションを書けば共有ライブラリを利用するようなライブラリ(Gem)を作ることができますが、FFIを使えばRubyで書くことができますしポータブルな実装になります。本家曰く:

実際FFIを使ってみると、単純なものなら一瞬でバインディングを作ることができるので非常に便利でした。慣れるまでは「このパターンだとどう記述すれば良いのだろう」というので悩んでしまいますが慣れ次第です。日本語で解説しているブログなどはほとんど見つからないですが、使い方は公式のドキュメントが割とよくできているので英語さえ読めればなんとかなります。このエントリでは公式チュートリアルにないようなちょっとだけ複雑なケースを含む、簡単なFFIの書き方を紹介したいと思います。

FFI導入部分

Ruby FFIffiを使うのでまず最初にインストールしておきます。基本的にはバインディングを割り当てるモジュールの中でFFI::Libraryを有効にしてffi_libで共有ライブラリを読み込むことです。例としては以下のような感じになります。

require 'ffi'

module FooLib
  extend FFI::Library
  ffi_lib "libfoo.so"

  ## ここに実装を書く 
end

この後はattach_functionを使って共有ライブラリの各関数を割り当てていくことになります。また、必要に応じて構造体やenumの定義を行います。

サンプル

まず簡単な例として以下のfoo.cfoo.hからlibfooを作成して、そのバインディングを作成する場合を紹介します。

// foo.c
static const char *foobar = "FOOBAR";

double func_a(int a, float b, unsigned long c, char *name) {
  printf("libfoo: func_a: %d %f %lx %s\n", a, b, c, name);
  return 0.1;
}
struct Foo* create_foo() {
  struct Foo *foo = (struct Foo *)malloc(sizeof(struct Foo));
  foo->name = (char *)foobar;
  foo->ptr = (int *)malloc(sizeof(int));
  *foo->ptr = 100;
  printf("libfoo: create_foo: %p\n", foo);
  return foo;
}
void use_foo(struct Foo *foo, int a) {
  printf("libfoo: use_foo: %p %d\n", foo, a);
  return;
}
void free_foo(struct Foo *foo) {
  printf("libfoo: free_foo: %p\n", foo);
  if (foo != NULL) {
    free(foo->ptr);
    free(foo);
  }
  return;
}
// foo.h
struct Foo {
  char *name;
  int *ptr;
};

double func_a(int a, float b, unsigned long c, char *name);
struct Foo* create_foo();
void use_foo(struct Foo *foo, int a);
void free_foo(struct Foo *foo);

func_aはいろんな型の引数を受け取ってdoubleを返すシンプルな関数です。create_foouse_foofree_fooはFoo構造体を操作するAPI群となります。

これをRuby FFIを使って割り当てたプログラムは以下のようなものになります。

module FooLib
  extend FFI::Library
  ffi_lib "libfoo.so"

  class Foo < FFI::Struct
    layout(
      :name, :string,
      :ptr, :pointer,
    )
  end

  attach_function :func_a, [:int, :float, :ulong, :string], :double
  attach_function :create_foo, [], :pointer
  attach_function :use_foo, [:pointer, :int], :void
  attach_function :free_foo, [:pointer], :void
end

attach_functionは共有ライブラリの関数をRubyのメソッドとして割り当てるメソッドです。第一引数に関数名、第二引数に関数の引数の型を配列で指定、第三引数で戻り値の型を指定します。

構造体を定義する場合はFFI::Structを継承してFFI::Struct#layoutでメンバーを定義します。メンバーの定義方法は他にもいくつかあるようですが、自分はいつもlayoutを使って定義しています。layoutは,名前、型の順でメンバーの個数分並べていきます。

以上で関数の割り当てができたので、このモジュールを使ってみます。プログラムは以下の通りです。

p FooLib.func_a(10, 1.0, 1 << 60, "foobar")
puts 

foo_ptr = FooLib.create_foo
p foo_ptr.class
p foo_ptr.address.to_s(16)
puts

foo = FooLib::Foo.new foo_ptr
p foo.class
p foo[:name].class
p foo[:name]
p foo[:ptr].class
p foo[:ptr].read_int
puts

FooLib.use_foo(foo, 10);
FooLib.free_foo(foo);

このプログラムを実行した結果は以下の通りです。

libfoo: func_a: 10 1.000000 1000000000000000 foobar
0.1

libfoo: create_foo: 0x7f7926dcf220
FFI::Pointer
"7f7926dcf220"

FooLib::Foo
String
"FOOBAR"
FFI::Pointer
100

libfoo: use_foo: 0x7f7926dcf220 10
libfoo: free_foo: 0x7f7926dcf220

func_aでは引数と戻り値が正しい値になっていることがわかります。create_fooでは戻り値の型を:pointerとしたため、FFI::Pointerクラスが返ってきています。FFI::StructinitializeFFI::Pointerを渡すことでインスタンスを作成できます。構造体の中身のデータを取得したい場合には一度変換する必要があります。use_foofree_fooの引数にはcreate_fooの戻り値をそのまま渡すこともできますが、FooLib::Fooを渡すこともできます。それぞれ渡されたポインタのアドレスが正しい値になっていることがわかります。

オブジェクトの自動的な解放

先ほどの例ではfree_fooを明示的に呼び出す必要があるため、メモリリークする可能性がありますし、何よりRubyっぽくないです。FFIにはオブジェクトが解放(GC)されるときに自動的にメソッドを呼び出してくれる機能があります。方法はいくつかありますが、自分はManagedStructもしくはAutoPointerを使った実装を使っています。

ManagedStructを使った解放

ManagedStructStructにリリース機能を付けただけで基本的にStructと変わりません。上記のLibFooモジュールの中でStructの代わりにManagedStructを使って以下のように書き換えます。

  class ManagedFoo < FFI::ManagedStruct
    layout(
      :name, :string,
      :ptr, :pointer,
    )
    def self.release(ptr)
      puts "release 0x#{ptr.address.to_s(16)}"
      FooLib::free_foo(ptr)
    end
  end

self.releaseGC時に自動的に呼び出されるメソッドで、クラスメソッドなのが特徴です。これをcreate_fooの戻り値からnewしてインスタンスを作成すると、そのインスタンスGCされるときに自動的にfree_fooが呼ばれるようになります。

スコープから外れてすぐGCされるわけではないので以下のようにループしておけばいずれGCされて確認できると思います。

loop do
  foo = FooLib::ManagedFoo.new FooLib.create_foo
  p foo.class
  FooLib.use_foo foo.pointer, 1000
end

AutoPointerを使った解放

AutoPointerPointerにリリース機能を付けたもので、StructにおけるManagedStructのようなものです。

AutoPointerself.releaseでポインタが渡されるのでそれを使って解放するだけですが、いろいろとメソッドを生やして、オブジェクトを操作する専用クラスっぽくすることもできます。実際には同じことをManagedStructを使って行うこともできます。

class FooPointer < FFI::AutoPointer
  def self.release(ptr)
    puts "release 0x#{ptr.address.to_s(16)}"
    FooLib::free_foo(ptr)
  end

  def initialize
    ptr = FooLib.create_foo
    super ptr
    @foo = FooLib::Foo.new ptr
  end

  def use
    FooLib::use_foo @foo.pointer
  end

  def name
    @foo[:name]
  end

  def ptr
    @foo[:ptr]
  end
end

FooPointerinitializeの中でcreate_fooを呼び出すようにしています。このインスタンスGCされる時にはself.releaseで自動的にfree_fooが呼び出されます。またその他に構造体の中身へのアクセッサを生やしたりしてカプセル化したりもできます。

型の可読性をあげる

attach_functionの引数や戻り値の型で:pointerとした場合に何のポインタかがわからないので、ここだけ見ても仕様がわからないという問題があります。

attach_function :create_foo, [], :pointer
attach_function :use_foo, [:pointer, :int], :void
attach_function :free_foo, [:pointer], :void

今回このポインタの型はstruct Fooなのでそれに対応するFooLib::Fooにひもづけることができます。

attach_function :create_foo, [], Foo.ptr
attach_function :use_foo, [Foo.ptr, :int], :void
attach_function :free_foo, [Foo.ptr], :void

:pointerの部分をFoo.ptrと置き換えることでFooのポインタとして受け渡しを行うという意味になります。

この状態でcreate_fooを実行すると戻り値の型はFFI::Pointerではなく、最初からFooLib::Fooで返ってことになります。

foo = FooLib.create_foo
p foo.class  # => FooLib::Foo

また、ポインタではなく値で受け渡しをしたい場合は#by_valueを使うことで値渡しを指定できます。

例えば以下のようにstruct Fooを値で受け取るuse_foo_value関数の場合は

void use_foo_value(struct Foo foo);

Foo.by_valueで値渡しを指定することができます。

attach_function :use_foo_value, [Foo.by_value, :int], :void

使用するときはFooLib::Fooインスタンスをそのまま引数に渡すだけで良いです。

foo = FooLib.create_foo
FooLib.use_foo_value foo

ポインタ渡しと配列

関数の使用として引数でポインタを渡して関数内で値を詰めて返すというものや、配列とそのサイズを渡して関数無いで指定サイズ分詰めて返すもの、そして配列のポインタを返すものがあると思います。

Cのコードとしては以下のような3つのパターンです。

void foo_int(int *a);
void foo_int_array(int *a, int num);
void foo_allocate_array(int **a, int *num);

void foo_int(int *a) {
  *a = 10;
}

void foo_int_array(int *a, int num) {
  for (int i = 0; i < num; i++)
    a[i] = 10;
}

void foo_allocate_array(int **a, int *num) {
  int n = 3;
  *a = (int *)malloc(sizeof(int) * n);
  for (int i = 0; i < n; i++)
    (*a)[i] = 10 + i;
  *num = n;
  printf("libfoo: foo_allocate_array: %p %d\n", *a, n);
}

これらの割り当ては以下のようになります。ポインタのポインタも:pointerとして割り当てます。

module FooLib
  extend FFI::Library
  ffi_lib "libfoo.so"

  attach_function :foo_int, [:pointer], :void
  attach_function :foo_int_array, [:pointer, :int], :void
  attach_function :foo_allocate_array, [:pointer, :pointer], :void
end

このモジュールを利用したプログラムは以下のようになります。

a1 = FFI::MemoryPointer.new :int             # int *a = malloc(sizeof(int))
FooLib.foo_int(a1)                           #
r1 = a1.read_int                             # ((int *)a)[0]
p r1                                         # 10

a2 = FFI::MemoryPointer.new(:int, 5)         # int *a = malloc(sizeof(int) * 5)
FooLib.foo_int_array(a2, 5)                  #
r2 = 5.times.map { |i|                       #
  a2.get_int(i * 4)                          # ((int *)a)[i]
}                                            #
p r2                                         # => [10, 11, 12, 13, 14]

arr_ptr = FFI::MemoryPointer.new :pointer    # int **a = malloc(sizeof(int *))
num_ptr = FFI::MemoryPointer.new :int        # int *num = malloc(sizeof(int))
FooLib.foo_allocate_array(arr_ptr, num_ptr)  # => libfoo: foo_allocate_array: 0x7f0d2052bd60 3
arr = arr_ptr.read_pointer                   # int *a = *(int **)a
num = num_ptr.read_int                       # int num = *(int *)num
p arr.address.to_s(16)                       # => "7f0d2052bd60"
p num                                        # => 3
r3 = num.times.map { |i|                     #
  arr.get_int(i * 4)                         # ((int *)a)[i]
}                                            #
p r3                                         # => [10, 11, 12]

FFI::MemoryPointercaallocのようなもので、第一引数に型、第二引数に個数を指定します。割り当てたメモリはインスタンスGCされる時に自動的に解放されます。 read_intのようにread_hogehogeでキャストしてからポインタを参照するといったような動作になります。 最初のパターンではread_intするだけで値がとれます。 二個目のパターンではint型の配列を作ってから渡します。get_intread_intと似ていますが、引数で指定したオフセットからポインタを参照することになります。 三個目のパターンではint配列のポインタとintのポインタを渡しますが、int配列のポインタの方はただのポインタのポインタとして渡します。arr_ptr.read_pointerで1段ポインタを参照し、arr_get_intで実際の値を取得します。

三個目のパターンでは共有ライブラリ側でメモリを割り当てているので、解放関数(例えばfoo_deallocate_array)を使う必要があるケースがあります。自分はこの場合には、foo_allocate_arrayの戻り値を管理するクラスをFFI::AutoPointerで作成してreleasefoo_deacllocate_arrayを呼び出すようにしています。このクラスでEnumerableArrayを継承すれば見た目上はArrayと同様の動作をして自動的に解放もしてくれるクラスになります。

class FooArray < FFI::AutoPointer
  class FooPointer < FFI::Pointer
    attr_reader :ptr
    attr_reader :num
    def initialize(ptr, num)
      super ptr
      @ptr = ptr
      @num = num
    end
  end

  include Enumerable

  def initialize(ptr, num)
    super FooPointer.new(ptr, num)
    @arr = ptr
    @num = num
  end

  def self.release(ptr)
    FooLib::foo_deallocate_array(ptr.ptr, ptr.num)
  end

  def each(&block)
    @num.times { |i|
      block.call(@arr.get_int(i * 4))
    }    
  end
end

arr_ptr = FFI::MemoryPointer.new :pointer    # int **a
num_ptr = FFI::MemoryPointer.new :int        # int *num
FooLib.foo_allocate_array(arr_ptr, num_ptr)
arr = arr_ptr.read_pointer                   # int *a
num = num_ptr.read_int                       # int num

foo = FooArray.new(arr, num)                 # foo_allocate_arrayの戻り値を管理するクラス
p foo.class                                  # => FooArray
p foo.map{|e| e + 100}                       # => [110, 111, 112]

exit                                         # => libfoo: foo_deallocate_array

最後に

Ruby FFIの紹介と実装する上での簡単なテクニック集的なものを紹介しました。実際にはまだEnum周りでいろいろとあるのですが長くなってきたのでここで止めます。FFIには更にGeneratorだとかConverterだとかが存在して、これらが何者なのかイマイチわかっていないですが、使いこなせるともっと簡単にかけるのかもしれません。

参考リンク

LLVM 3.4 リリースノートの訳

半年に一回がんばる日が来ました。例によってLLVMのリリースノートの訳です。 直訳がほとんどなので気になる人は原文を読みましょう。

12月31日時点でLLVMのダウンロードページやドキュメントページへのリンクがありませんが、直接それっぽいURLにアクセスすると既に存在するようです。ただしClangはまだ無いようです。

イントロダクション(Instroduction)

このドキュメントはLLVMコンパイラ・インフラストラクチャのリリース3.4に関するリリースノートです。前回のリリースからの大きな改善点, サブプロジェクトの改善点, コードの利用者などを含むLLVMの状況を記しています。LLVMリリースの全てはLLVMリリースサイト からダウンロードできます。

LLVMについての最新情報などの詳細はLLVMメインサイト を参照して下さい。疑問や感想などがありましたらLLVM開発者メーリングリスト に投稿して下さい。

SubversionチェックアウトもしくはメインLLVMウェブページからこのファイルを読んでいるなら、このドキュメントには現在のリリースではなく次のリリースが適用されることに注意して下さい。特定のリリースのリリースノートをみたい場合、リリースページ を見て下さい。

本リリースの包括的では無い変更点リスト(Non-comprehensive list of changes in this release)

  • これがC++98ツールチェインを使用してコンパイルするLLVMの最後のリリースなると期待されています。私たちはこのリリース後からC++11の機能をLLVMと他のサブプロジェクトで使い始めることを期待しています。とはいえ、全てのプラットフォーム上のホストコンパイラとして現代的なC++ツールチェインの合理的なセットをサポートすることを約束します。これには最低でもWindowsのVisual Studio 2012とMacおよびLinuxでのClang 3.1とGCC 4.7.xを含んでいます。最終的なコンパイラセットとそれらがサポートするC++11機能は確定してはいない。しかし、LLVMの利用者が次のリリースでホストツールチェインの要求における大きな変化を追従してくれることを望んでいます。
  • パイプライン上でのいかなるコマンドの失敗によっても回帰テストが失敗するようになります。あるディレクトリで無効にするにはconfig.pipefail = Falselit.local.cfgに追加して下さい。詳細はLitを参照して下さい。
  • 古いJITから例外ハンドリングのサポートが削除されました。EH(例外ハンドリング)のサポートが必要であればMCJITを利用して下さい。
  • R600バックエンドが実験的とはされなくなり、デフォルトでビルドされるようになりました。
  • APFloat::isNormal()APFloat::isFiniteNonZero()に、APFloat::isIEEENormal()APFloat::isNormal()にリネームされました。これはAPFloat::isNormal()がIEEE-754R-2008に準拠することを保証します。
  • ライブラリ呼び出し簡易化パス(library call simplification pass)が削除されました。その機能は命令結合器(instruction combiner)と関数属性マークパス(function attribute marking pass)に統合されました。
  • Visual Studio 2008を使用するビルドのサポートが廃止されました。VS2010かそれ以降を使用して下さい。詳細な情報はGetting Started using Visual Studioを参照して下さい。
  • 以前-O3で有効になっていたループベクトル化が-Os-O2で有効化されるようになりました。
  • SLPベクトル化がデフォルトで有効化されるようになりました。
  • llvm-arが新しいオブジェクトライブラリを使用してGNUフォーマットでアーカイブとシンボルテーブルを生成するようになりました。
  • FileCheckが複数回の-check-prefixの指定を許容するようになりました。これにより複数のRUN行を使うときに重複チェックを減らせるようになります。
  • bitcast命令が異なるアドレス空間を持つポインタ間のキャストをもはや許容しなくなりました。これを行う場合は、addrspacecast命令を使用して下さい。
  • 異なるアドレス空間のための異なるサイズのポインタが多くの場合動作するようになりました。これは主にGPUターゲットで役に立ちます。
  • OCamlバインディングがほとんど全てのLLVMライブラリをカバーするように拡張されました。

Mips Target

MIPS SIMDアーキテクチャ(MSA)のサポートが追加されました。MSAはインラインアセンブラ__builtin_msaのプリフィックスを持つをintrinsicsおよび通常のコード生成をサポートしています

(命令セットなどのドキュメントを含む)MSAに関する情報は、MIPS SIMD page at Imagination Technologiesを参照して下さい。

PowerPC Target

PowerPCバックエンドには以下の変更点を含みます:

  • fast-iselサポート(より高速な-O0のコード生成)
  • 組み込みアセンブラの多くの改善
  • アラインされていない(Altivec)ベクトル読み込みの生成のサポート
  • fcpsgn命令の生成のサポート
  • round()(nearbyint()rint())ではなく、fast-mathモード内でのみ行われる)のためのfrinの生成
  • (A2のような)組み込みコアのための命令スケジューリングの改善
  • (特に32bitモードにおける)prologue/epilogue生成の改善
  • 動的なスタックアラインメント(と大きなアラインメントを持つ動的なスタック割り当て)のサポート
  • counter-register-basedループの生成の改善
  • バグ修正

SPARC Target

SPARCバックエンドは多くの改善があります。それは、

  • 実験的なSPARC V9バックエンド
  • SPARCJITサポート
  • fp128サポート
  • 例外ハンドリング
  • TLSサポート
  • leaf functionの最適化
  • バグ修正

SystemZ/s390x Backend

LLVMとclangはzEnterprise z196とzEnterprise EC12ターゲットを最適化できるようになりました。clangではそれぞれ-march=z196-march=zEC12を使用することでこれらのターゲットが選択されます。

LLVM3.4を利用した外部オープンソースプロジェクト(External Open Source Projects Using LLVM 3.4)

LLVMの興味深い点は多くの他の言語やツールプロジェクトのためのイネーブル技術として使われることです。このセクションではLLVM3.4で動作するよう更新されたいくつかのプロジェクトをリストアップします。

DXR

DXRはFixfoxのような大きなプロジェクトを理解するためのMozillaのコード検索とナビゲーションツールです。"この関数の全ての呼び出し元を探し出せ"といった構造化クエリと同様な全文検索正規表現検索をサポートしています。浦ではtrigram indexやre2ライブラリそしてclangコンパイラプラグインによって収集された構造化データを使用しています。

LDC - the LLVM-based D compiler

D はCライクの構文と静的型付きを持つ言語です。これは効率、制御、そしてモデリング力を安全性とプログラマの生産性に実際的に結びつける。D言語はコンパイル時関数実行(CTFE)とテンプレートメタプログラミングのような強力なコンセプトをサポートし、そして並列性への革新的なアプローチと多くの古典的なパラダイムを提供する。

効率的なネイティブコードを生成するために、LLVMと結合したリファレンスコンパイラをバックエンドとして、[LDC](http://wiki.dlang.org/LDC] はそのフロントエンドを使用する。LDCLinuxOS X、そしてWindowsのようなx86/x86_64システムとLinux/PPC64をターゲットとする。ARMやAArch64のような他のアーテキテクチャへのポートは進行中です。

LibBeauty

LibBeautyコンパイラリバースエンジニアリングツールは現在LLVM disassemblerとLLVM IR Builderを利用しています。このプロジェクトの現在の目標はx86_64バイナリ.oファイルを入力として受け取り、同様のLLVM IR.bcもしくは.llファイルを出力として生成することである。ARMバイナリ.oファイルの入力のサポートは後に追加されるでしょう。

Likely

Likelyは画像認識のためのオープンソースの領域特価型言語です。アルゴリズムはLLVMのMVJIT基盤を使用してjust-in-timeコンパイルされ、GPUが有効になったOpenCL SPIRやCUDAとシングルもしくはマルチスレッドCPU上で実行されます。Likelyは画像処理や統計的な学習カーネルが一般的に行列データ型を扱うように書く必要があり、実行時にはそれらが同じ型上で処理を繰り返す傾向があるという知見を活かしている。

Portable Computing Language (pocl)

容易にポータブルなオープンソースOpenCL実装を生成することに加えて、pocl のもう一つの主な目的はコンパイラの最適化を利用したOpenCLプログラムの性能ポータビリティを改善することとターゲット依存の手動最適化の必要性を減らすことです。poclの重要な部分は、ワークグループバリアの存在の中でさえカーネルコンパイラを用いて静的に多数のワークアイテムを並列化するLLVMパスのセットであることです。これによって複数の方法でワークグループの静的な細流度並列化が可能になる。

Portable Native Client (PNaCl)

Portable Native Client (PNaCl)はアプリケーションのポータビリティやセキュリティの恩恵を犠牲にすることなく、ネイティブコードの性能と低レベル制御を現代のウェブブラウザにもたらすためのChrome主導のプロジェクトです。PNaClはネイティブのCとC++コードをLLVM clangコンパイラを使用して中間表現にコンパイルすることで動作する。この中間表現はポータブルな実行形式へとラップされたLLVMバイトコードのサブセットである。これは他のウェブサイト資源と同様にウェブサーバ上でホストされることができる。サイトがアクセスされると、Chromeがそのポータブルな実行形式を取得して下層デバイスに最適化されたアーキテクチャ依存のマシンコードへの翻訳する。PNaClは開発者に彼らのコードをどんなハードウェアプラットフォームでも動作するコンパイルさせ、下層のCPUやGPUの力を活用しながらそのPNaClアプリケーションをウェブサイト上に組み込ませる。

TTA-based Co-design Environment (TCE)

TCE はTransport triggered architecture(TTA)基にした新しいプロセッサを設計するためのツールセットです。これはC/C++プログラムから合成可能なVHDL/Verilogそして並列プログラムバイナリまでの完全な協調設計フローを提供する。プロセッサの設定可能な項目としてはレジスタファイル、機能ユニット、サポート操作、そして相互接続ネットワークが含まれる。

TCEはC/C++/OpenCL Cの言語サポート、ターゲット非依存の最適化、そして一部のコード生成のためにClangとLLVMを利用する。これはコンパイラツールチェインの大部分をターゲット毎に再コンパイルすることを避けるために、デザインされたTTAプロセッサ向けの新しいLLVMベースのコード生成器を"オンザフライ"で生成し、それらを実行時ライブラリとしてコンパイラバックエンドにロードする。

WebCL Validator

WebCL ValidatorはOpenCL ES 1.1のサブセットであるWebCL C言語のための検証を実装している。バリデータはWebCL Cの正確さを確認し、source-2-source変換としてメモリ保護を実装する。その変換によりWebCLはメモリ保護されたOpenCLへと変換される。保護されたOpenCLは割り当てられていないメモリ範囲へのアクセスができず、他のプログラムへ情報がリークしないようにそのメモリは常に初期化される。

Additional Information

LLVMウェブページ 、特にドキュメントでは様々な追加情報が利用できます。ウェブページにはSubversionにあるソースコードにあわせて更新されているAPIドキュメントもあります。LLVMツリーの "llvm/docs" ディレクトリに行くと特定のリリースに対するドキュメントが取得可能です。

LLVMに関する質問やコメント等がありましたらメーリングリスト を通してお気軽にご連絡下さい。

きつねさんとおぼえる!Clang おかわりをC85で出します

f:id:sabottenda:20131221195429j:plain:h300

前回に引き続きClang本を出します。
タイトルは「きつねさんとおぼえる!Clang おかわり」です。
タイトル案を適当にネタで出したらそのまま通って驚愕しているところでございます。

前回はプラットフォームとしてのClangの機能を駆け足で説明しましたが、
今回もテーマとしては同じでLibClangとLibToolingについて重点的に説明しています。
前回購入した方でも十分新しい内容が入っていますので、ぜひ立ち寄って頂ければと思います。 3,4章のLibClangについては風薬が担当し、5章のLibToolingと全体の構成を柏木餅子(id:motipizza, @sui_moti)が担当しています。
表紙や挿絵(!)は矢上さん(@re_sonanz, web)にお願いしています。

LibToolingを使った代表的なツールであるclang-formatやclang-modernizeの紹介から始まり、
4章ではLibClangの中でも抽象構文木にフォーカスしてRubyバインディングを使って詳しく解説し、
5章でLibToolingを使ってclang-degraderというものを作成しながら解説するという内容になってます。

以下、頒布場所の詳細になります。部数制限等は特にありません。

毎回売り切れになってしまって申し訳なかったので今回は余らせる勢いで200部刷ってあります。
前回の「きつねさんとおぼえる!Clang」も50部刷りますので買い逃した方はぜひ両方ともご購入下さい。

  • はしがき
  • はじめに
    • 本書の構成
    • 本書の目的
    • 本書内の記載について
    • お断り
  • 環境構築とClang の使い方
    • 本章の概要と構成
    • 環境
    • インストール
      • ビルドに必要なパッケージのインストール
      • LLVM/Clang のビルド/インストール
      • パスの設定
    • ツールの種類と確認
      • clang
      • clang-check
      • clang-format
      • clang-modernize
  • LibClang AST の概要
    • Clang の抽象構文木
    • LibClang とは
    • LibClang における抽象構文木の構造
    • 抽象構文木の操作関数
      • インデックスと翻訳単位の操作
      • 翻訳単位とカーソル間の操作
      • ソース位置と物理ファイル位置間の操作
      • ソース位置とカーソル間の操作.
      • ソース位置とソース範囲間の操作
      • カーソル間の移動
      • カーソルの文字列表現
      • カーソルの種類
      • 型に関する操作
      • コメントの取得
  • LibClang チュートリアル
    • LibClang の言語バインディングについて
    • LibClang のクラスとRuby のクラスの対応
    • 翻訳単位を作成する
    • 抽象構文木を走査する
    • 注釈関数を作ってみる
    • ファイル位置を指定してカーソルを取得する
    • 定義と宣言を取得する
    • typedef された型を参照する
    • マクロ展開の情報を取得する
    • コメントを取得する
  • LibTooling おかわり!
    • まえおき
    • 復習しましょう
      • LibTooling ってなんだっけ?
      • 前回のおさらい
    • 今回は何するの?
    • リファクタリング情報をファイル出力する
      • YAML::Output で出力を試す
      • TranslationUnitReplacements に変更する
      • MatchCallback とNullptrDegrader の定義
      • main 関数とoutputRepacementsYAML の修正
      • コンパイルと実行
    • 指定した範囲のみ適用する
      • Degrader クラスとFixer クラスの修正
      • CommandLine ライブラリ
      • オプションのパース
      • コンパイルと実行
  • あとがき
  • 著者/関係者一覧

Fluentdの知られていない6つのこと

Fluentdの知られていない6つのこと

本当に知られていないかはわからないです。 公式にはあまり説明されていなかったり調べてもなかなか見つからないことが多いと個人的に思ったものを集めました。 機能や言葉の細かい説明は省いているのである程度使っている人が対象です。

out_copyはshallow copy

Fluentdで最初に使うであろうビルドインされているout_copyプラグインですが、実はデフォルトではメッセージをdeep copyしないため意図しない結果になることがあります。

<match test>
  type copy
  <store>
    type record_modifier
    tag test.aa
    foo bar
  </store>
  <store>
    type retag
    tag test.bb
  </store>
</match>

<match test.{aa,bb}>
  type stdout
</match>

例えばこのような設定の場合、test.aa のみに foo=bar が追加されることが期待されます。 しかし実際には test.bb にも foo=bar が追加されてしまいます。 これはout_copyプラグインがshallow copyになっているためです。

この問題を解決するためのトリッキーな方法としてメッセージを加工するプラグインは最後に持ってくるなどの方法があったりしますが、処理する順番が今後も記述順のままである保証もないので微妙なところです。

現最新版の v0.10.39 ではout_copyプラグインでdeep copyを行う deep_copy が追加されました。 デフォルトではoffになっているので、必要に応じて on にしましょう。

TCP Heartbeat

Fluentdの肝であるout_forwardは接続先の生存確認(heartbeat)のために定期的にパケットを送信しています。 このパケットの送信はUDPで行われているため、環境によってはTCPは開いてるけどUDPは閉じてるということがあり、別の環境では動いたのにこの環境では動かない理由がわからない…などということがあったりします。

実はこの生存確認の方法も heartbeat_type オプションで指定することが可能で、:tcp とすることでTCPによる確認が可能になります。 自分は最近まで知らなかったですが、結構前から実装されているので気軽に使っても良さそうです。

secondaryは実はなんでも使える

公式のドキュメントでは out_forwardプラグイン のオプションとして説明されている secondary オプションですが、細かい説明もないのでデフォルトのままなんとなく使っているということも多いと思います。しかし、 この secondary オプションは、使いこなせると更に強固なシステムを構築することもできるので、ぜひ知っておいた方が良いです。

まず、 secondary オプションは out_forwardプラグイン以外にも使うことができます。 もともとこのオプションの目的が"BufferedOutputプラグインでチャンクに入りきらなくなったときにどうするか"というもののため、BufferedOutputプラグインであれば全てのプラグインで使うことができます。 out_forwardに限らず、詰まる可能性があるところでは使っておいた方が良いでしょう。

次に、 secondary オプションで指定できるtypeは file だけではありません。 secondaryで指定する設定はOutputプラグインの設定と全く同じなので、実はどんなプラグインでも書くことはできます。つまりtypeにfileを指定した場合は out_fileプラグインの全てのオプションを使うことができます。 ただし、内部の実装を完全に理解している人以外はout_file以外を使うべきではありません。 その理由はBufferedOutputのバッファの扱い方がプラグインにより異なるため、組み合わせによってはうまく動作しません。というよりほとんどの場合そのまま使えるプラグインはないでしょう。

このあたりはFluentd本体を改善することでなんとかなりそうですがまだ先は長そうです。

out_fileのログに対してシンボリックリンクを生成する

out_fileプラグインを使って最初に驚くことがログファイル名です。 オプションでフォーマットを指定できるので、例えば time_slice_format %Y%m%d のように指定したら foo.20130928.log とかになるんだろうなーと思ったら、実際には foo.20130928.b4e76e103eebf8c2f とか謎のsuffixがついて困るということがあります。

大抵のログローテーション付きログの仕組みにあるように、Fluentdにも最新のログファイルに対するシンボリックリンクを貼る機能があります。 path オプションと同様に symlink_path オプションでシンボリックリンクのパスを指定すると常に最新のファイルを差し続けるくれます。 ただし、実装の仕様上一時的にシンボリックリンク先がなくなることがことがあります。その場合も新しいログができると自動的にリンク先を変えてくれます。 シンボリックリンクを参照するときには tail -F で見るとよいでしょう。

こちらも最新版の v0.10.39 から利用することができます。

num_threadsで並列処理数を指定できる

BufferedOutputを並列に行うためのオプションとしてnum_threadsがあります。 ただし、このオプションは誰も使っていない可能性すらあります。

BufferedOutputプラグインで num_threads を指定することで、writeの処理を並列に行うことができます。実装としてはキューにたまった処理を並列に取り出して各スレッドで処理するというようになっていて、見た目上は問題無さそうです。今のところ自分ではまだ試していないです。

ソースコードを読んでいて少し気になったところとしては、try_flushでキューが空じゃないとエンキューしないので、chunk_limitを頻繁に超えるようなものじゃないと複数スレッドあってもデキューできないような気がしました。

BufferedOutputをバルク処理以外に使うのは微妙

BufferedOutputの特徴としてリクエストのバッファリングと再送機能によってリクエストの送信を保証できるということが挙げられます。 よって、失敗する可能性のある処理は全てBufferedOutputで行いたいと思います。

BufferedOutputは溜まったリクエストを一括で処理することを想定している(ように思える)ので、個別のリクエストを処理するようなプラグインを書くのは注意が必要です。 その理由は再送処理の粒度がチャンク単位となっているからです。 5つのリクエストがバッファに溜まっている時に5回のリクエストに分けて送信するとします。この時に、2つ目目までは成功したけど3つ目で失敗して再送処理をFluentdに任せると、失敗した3つめからではなく最初のリクエストからやりなおしになります。 よって、リクエストを個別に行う場合は冪等性が保証されている必要があります

この問題を解決しようと汎用的なOutputクラスを作ろうとしてましたが、中の仕組みを保ったまま作るのが意外と難しいので止まってます。 誰か作ってください!

さいごに

Fluentdは使うのが簡単で機能も十分にありますが、やはり使っていく上でプラグインを書いたり安全性を確認したりすると内部の実装を知る必要が出てきます。 実装はそれほど複雑ではなく行数も短いためすぐに読めると思います。 読んでみると改善すべき点も意外といっぱいあったりするので、使っている人は読んで貢献していきましょう。

記事に間違い等ありましたらご連絡下さい。

きつねさんとおぼえる!ClangをC84で出します

きつねさんシリーズ第三弾としてClang本を出します。
タイトルは「きつねさんとおぼえる!Clang」です。

今までLLVMを扱っていましたが、今回からClangについて紹介していく予定です。
今回はあまり時間が取れなかったため応用的な内容までは紹介することはできていませんが、
Clangがどのような機能を提供しているかというのを理解できると思います。
Sanitizer、LibClang、LibTooling、ClangPluginを大きなテーマとして紹介しています。
自分がSanitizerとLibClangを担当し、残り2つを餅さんが担当しています。

表紙はいつも通り矢上さん(web)に書いて頂きました。
表紙を見て頂ければわかる通り、新しいキャラクタとしてアンが登場しています。
更に今回は表紙に追加して、今まで登場したキャラクタの設定資料集がついています。
これだけでも十分買って良かった!と思えるものになってます。

以下、頒布場所の詳細になります。部数制限等は特にありません。
コミックマーケット84
場所:東京ビッグサイト
サークル名:MotiPizza
日程:3日目 8月12日(月)
スペース番号:東ペ-04a
値段: 1部 700円
ページ数: 102ページ
部数制限: 無し
合計部数: 150部

以下、章構成。

  • はしがき
  • はじめに
    • 本書の構成
    • 本書内の記載について
    • お断り
  • 環境構築とClang の使い方
    • 本章の概要と構成
    • 環境
    • インストール
    • ツールの種類と確認
  • はじめてのSanitizer
    • Sanitizer とは
    • Sanitizer の種類
  • LibClang
    • LibClang でできること
    • 翻訳単位について
    • トークンの取得
    • 抽象構文木の走査
    • 診断情報の取得
    • コード補完情報の取得
    • コンパイルデータベース
  • LibTooling
    • ビルド環境の構築
    • Clang AST の概要
    • LibTooling で簡単なツールを作る
    • RecursiveASTVisitor でAST の情報を出力する
    • LibAstMatchers で特定パターンのAST を捕捉する
    • RefactoringTool でコードを書き換えてみる
  • ClangPlugin
    • ClangPlugin で簡単なプラグインを作成する
  • あとがき
  • 著者/関係者一覧
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy