IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

DTrace の D 言語の文法と使い方のメモ

なんか、中途半端になってしまった。。。読まないほうがいいです。


個人的なメモ
だらだら書く

動機

pthread の確認するときに dtrace が凄く便利だったので
でも、全然使いこなせてない。(特に D 言語の文法が分からない)
dtrace のトレースの方法を書く言語を D 言語というが、一般的な D 言語とは違うもの。

dtrace の文法も含めて細かくかかれてる資料

これにつきる
http://docs.sun.com/app/docs/doc/817-6223?l=ja
さすが sun 。
この資料には日本語のはない。(6/17 現在)
文法定義ファイルも見つけた
http://www.opensource.apple.com/darwinsource/Current/dtrace-48/libdtrace/dt_grammar.y

Hello, world!

以下を hello.d で保存

BEGIN {
    trace("Hello, world!");
    exit(0);
}

実行

$ sudo dtrace -s hello.d 
dtrace: script 'hello.d' matched 1 probe
CPU     ID                    FUNCTION:NAME
  1      1                           :BEGIN   Hello, world!                    

文法

これ を見ながら

プログラム全体
d_program: d_expression DT_TOK_EOF { return (dt_node_root($1)); }
	|	d_program DT_TOK_EOF { return (dt_node_root($1)); }
	|	d_type DT_TOK_EOF { return (dt_node_root($1)); }
	;

d_program:	DT_CTX_DPROG { $$ = dt_node_program(NULL); }
	|	DT_CTX_DPROG translation_unit { $$ = dt_node_program($2); }
	;

D 言語をファイルに書いた場合、最初のトークンは DT_CTX_DPROG になる(実際に書いてある訳じゃないが、 Lexer がそういうトークンを返してくる)のでプログラム全体の文法は translation_unit を見ればいい。

translation_unit:
		external_declaration
	|	translation_unit external_declaration { $$ = LINK($1, $2); }
	;

external_declaration:
		inline_definition
	|	translator_definition
	|	provider_definition
	|	probe_definition
	|	declaration
	;

translation_unit (つまり、プログラム全体)は以下のいずれかの繰り返しということになる。

  • inline_definition
  • translator_definition
  • provider_definition
  • probe_definition
  • declaration

inline_definition, translator_definition, provider_definition については使ったことがないので、無視する。
あ、でも translator_definition は便利そうなので後で解説するかも(余力があれば)

probe_definition
probe_definition:
		probe_specifiers {
			/*
			 * If the input stream is a file, do not permit a probe
			 * specification without / <pred> / or { <act> } after
			 * it.  This can only occur if the next token is EOF or
			 * an ambiguous predicate was slurped up as a comment.
			 * We cannot perform this check if input() is a string
			 * because dtrace(1M) [-fmnP] also use the compiler and
			 * things like dtrace -n BEGIN have to be accepted.
			 */
			if (yypcb->pcb_fileptr != NULL) {
				dnerror($1, D_SYNTAX, "expected predicate and/"
				    "or actions following probe description\n");
			}
			$$ = dt_node_clause($1, NULL, NULL);
		}
	|	probe_specifiers '{' statement_list '}' {
			$$ = dt_node_clause($1, NULL, $3);
		}
	|	probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED {
			dnerror($3, D_SYNTAX, "expected actions { } following "
			    "probe description and predicate\n");
		}
	|	probe_specifiers DT_TOK_DIV expression DT_TOK_EPRED
		    '{' statement_list '}' {
			$$ = dt_node_clause($1, $3, $6);
		}
	;

probe_specifiers:
		probe_specifier_list { yybegin(YYS_EXPR); $$ = $1; }
	;

probe_specifier_list:
		probe_specifier
	|	probe_specifier_list DT_TOK_COMMA probe_specifier {
			$$ = LINK($1, $3);
		}
	;

probe_specifier:
		DT_TOK_PSPEC { $$ = dt_node_pdesc_by_name($1); }
	|	DT_TOK_INT   { $$ = dt_node_pdesc_by_id($1); }
	;

ちょっと長いが probe_definition は以下のようなものだということが分かる

<(DT_TOK_PSPEC または DT_TOK_INT) がコンマ区切りで続いたもの> [ DT_TOK_DIV <式(expression)> DT_TOK_EPRED ] {
    <複数の文(statement_list)>
}

Lexer を見ると DT_TOK_PSPEC は hoge:hoge:hoge:hogehogehoge*::hoge のような文字、 DT_TOK_INT は 1234 のような数字 DT_TOK_DIV および DT_TOK_EPRED はスラッシュだと分かる
つまり、最初の Hello world の例は DT_TOK_DIV から DT_TOK_EPRED を省略したこの形だ

BEGIN {
  trace("Hello, world!");
}
式(expression)

expression は

expression:	assignment_expression
	|	expression DT_TOK_COMMA assignment_expression {
			$$ = OP2(DT_TOK_COMMA, $1, $3);
		}
	;
:
hogehoge_expression がずーっと続く。演算子の優先順位は C 言語と全く同じ
:

なるほど、あるコンテキストでは d 言語は式だけの場合があるということか。
ちょっと気になったところは
配列アクセスの添字も関数呼出しの引数のように、複数指定できる。

/* 配列アクセスの添字 */
array_parameters:
		/* empty */ 		{ $$ = NULL; }
	|	constant_expression	{ $$ = $1; }
	|	parameter_type_list	{ $$ = $1; } /* ←注目! */
	;

/* 関数呼出しの引数 */
function_parameters:
		/* empty */ 		{ $$ = NULL; }
	|	parameter_type_list	{ $$ = $1; } /* ←注目! */
	;

つまり、 dtrace では多次元配列を

array[index_1, index_2]

というようにアクセスするってことか。

複数の文(statement_list)
statement_list:	statement { $$ = $1; }
	|	statement_list ';' statement { $$ = LINK($1, $3); }
	;

statement:	/* empty */ { $$ = NULL; }
	|	expression { $$ = dt_node_statement($1); }
	;

うお。めっちゃシンプル!
え? D 言語って式文しかないの?? if 文 for 文 while 文は??
マジでないみたいだ
まあ、考えることは少なくなった、文は式文でしかない。
セミコロンとカンマの違いはほとんどない(値を持つかどうかぐらい)。
あ。変数宣言という文もない
変数宣言は probe_definition の外に書く。
プログラム全体のところで述べた declaration が変数宣言を含む。

宣言(declaration)
declaration:	declaration_specifiers ';' {
			$$ = dt_node_decl();
			dt_decl_free(dt_decl_pop());
			yybegin(YYS_CLAUSE);
		}
	|	declaration_specifiers init_declarator_list ';' {
			$$ = $2;
			dt_decl_free(dt_decl_pop());
			yybegin(YYS_CLAUSE);
		}
	;

declaration_specifiers:
		d_storage_class_specifier
	|	d_storage_class_specifier declaration_specifiers
	|	type_specifier
	|	type_specifier declaration_specifiers
	|	type_qualifier
	|	type_qualifier declaration_specifiers
	;

d_storage_class_specifier:
		storage_class_specifier
	|	DT_KEY_SELF { dt_decl_class(DT_DC_SELF); }
	|	DT_KEY_THIS { dt_decl_class(DT_DC_THIS); }
	;

storage_class_specifier:
		DT_KEY_AUTO { dt_decl_class(DT_DC_AUTO); }
	|	DT_KEY_REGISTER { dt_decl_class(DT_DC_REGISTER); }
	|	DT_KEY_STATIC { dt_decl_class(DT_DC_STATIC); }
	|	DT_KEY_EXTERN { dt_decl_class(DT_DC_EXTERN); }
	|	DT_KEY_TYPEDEF { dt_decl_class(DT_DC_TYPEDEF); }
	;

type_specifier:	DT_KEY_VOID { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("void")); }
	|	DT_KEY_CHAR { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("char")); }
	|	DT_KEY_SHORT { $$ = dt_decl_attr(DT_DA_SHORT); }
	|	DT_KEY_INT { $$ = dt_decl_spec(CTF_K_INTEGER, DUP("int")); }
	|	DT_KEY_LONG { $$ = dt_decl_attr(DT_DA_LONG); }
	|	DT_KEY_FLOAT { $$ = dt_decl_spec(CTF_K_FLOAT, DUP("float")); }
	|	DT_KEY_DOUBLE { $$ = dt_decl_spec(CTF_K_FLOAT, DUP("double")); }
	|	DT_KEY_SIGNED { $$ = dt_decl_attr(DT_DA_SIGNED); }
	|	DT_KEY_UNSIGNED { $$ = dt_decl_attr(DT_DA_UNSIGNED); }
	|	DT_KEY_STRING {
			$$ = dt_decl_spec(CTF_K_TYPEDEF, DUP("string"));
		}
	|	DT_TOK_TNAME { $$ = dt_decl_spec(CTF_K_TYPEDEF, $1); }
	|	struct_or_union_specifier
	|	enum_specifier
	;

type_qualifier:	DT_KEY_CONST { $$ = dt_decl_attr(DT_DA_CONST); }
	|	DT_KEY_RESTRICT { $$ = dt_decl_attr(DT_DA_RESTRICT); }
	|	DT_KEY_VOLATILE { $$ = dt_decl_attr(DT_DA_VOLATILE); }
	;

struct_or_union_specifier:
		struct_or_union_definition struct_declaration_list '}' {
			$$ = dt_scope_pop();
		}
	|	struct_or_union DT_TOK_IDENT { $$ = dt_decl_spec($1, $2); }
	|	struct_or_union DT_TOK_TNAME { $$ = dt_decl_spec($1, $2); }
	;

struct_or_union_definition:
		struct_or_union '{' { dt_decl_sou($1, NULL); }
	|	struct_or_union DT_TOK_IDENT '{' { dt_decl_sou($1, $2); }
	|	struct_or_union DT_TOK_TNAME '{' { dt_decl_sou($1, $2); }
	;

init_declarator_list:
		init_declarator
	|	init_declarator_list DT_TOK_COMMA init_declarator {
			$$ = LINK($1, $3);
		}
	;

init_declarator:
		declarator {
			$$ = dt_node_decl();
			dt_decl_reset();
		}
	;

declarator:	direct_declarator
	|	pointer direct_declarator
	;

direct_declarator:
		DT_TOK_IDENT { $$ = dt_decl_ident($1); }
	|	lparen declarator DT_TOK_RPAR { $$ = $2; }
	|	direct_declarator array { dt_decl_array($2); }
	|	direct_declarator function { dt_decl_func($1, $2); }
	;

lparen:		DT_TOK_LPAR { dt_decl_top()->dd_attr |= DT_DA_PAREN; }
	;

つまり

( 記憶クラス or 型 or const volatile など) の繰り返し

( 記憶クラス or 型 or const volatile など) の繰り返し 識別子;
( 記憶クラス or 型 or const volatile など) の繰り返し *識別子;
( 記憶クラス or 型 or const volatile など) の繰り返し 識別子(パラメータリスト);
( 記憶クラス or 型 or const volatile など) の繰り返し 識別子[パラメータリスト];
:
などなど
:

みたいな変数宣言と型宣言の組み合わせなのか。
型宣言は分かるけど変数宣言もグローバル領域にしかできないのか。

文法以外のいろいろ

  • C 言語のポインタは数値としてしか扱えない。
  • copyin でメモリの内容をコピーする
    • copyin(arg0, 3) ← C 言語のポインタ arg0 から 3 バイトコピーした領域の(D 言語の)ポインタを返す。
    • 解放とか考えなくてもいい
  • 0 終端な文字列は copyinstr(p) で
  • struct hoge { int fuga; } など同じ型を宣言して copyin と sizeof でコピーすればいい