SystemTapの使い方(その2)

#1 はじめに
systemtapの使い方の続編です。

インストール方法やその他の使い方は、下記を参照ください。
[SystemTapの使い方](https://qiita.com/hana_shin/items/9b265b4f9a51f98d0f4d)
[SystemTapの使い方(グルモード編)](https://qiita.com/hana_shin/items/f2806e68493c89fbbef8)
[SystemTapの使い方（User-Space Probing)](https://qiita.com/hana_shin/items/6bca693206bf5f887cb3)
[SystemTapの使い方(tapset編)](https://qiita.com/hana_shin/items/0f82ea9d8bc8ed5fd70b)

本記事の説明は、説明を簡単にするため、すべて`begin`プローブ内でスクリプトを実行しました。
本記事の説明は、`begin`以外の`end`プローブ内や本体の部分でも実行できます。

#2 環境
VMware Workstation 14 Playerで作成した仮想マシンを使用しました。
仮想マシンのOS版数は以下のとりです。

```console:OS版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

[root@server ~]# uname -r
3.10.0-957.el7.x86_64
```

#3 連想配列(Associative arrays)の使い方
連想配列とは、添え字にスカラー数値以外のデータ型(文字列型等)も使用できる配列のことです。
SystemTapの連想配列の構文は、`配列名[インデックス]`になります。
さらに、インデックスはカンマ区切りで最大9個までの値を指定できます。
たとえば、`test["Hello"]`や`foo[1,2,3,4,5,6,7,8,9]`のような配列を定義することができます。


##3.1 連想配列への値の代入方法、参照方法
配列testに10、配列fooに20を代入します。
そして、代入した値を表示してみます。

```console:サンプル
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test
global foo

probe begin
{
  test["Hello"] = 10
  foo[1,2,3,4,5,6,7,8,9] = 20

  printf("test=%d,foo=%d\n",test["Hello"], foo[1,2,3,4,5,6,7,8,9])
  delete test
  exit()
}
```

```console:実行結果
[root@server ~]# stap tp.stp
test=10,foo=20
```




##3.2 連想値(associated value)のインクリメント、デクリメント
配列testに10、配列fooに20を代入します。
次に、testは1インクリメント、fooは1デクリメントします。
そして、連想値を表示してみます。

```console:サンプル
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test
global foo

probe begin
{
  test["Hello"] = 10
  foo[1,2,3,4,5,6,7,8,9] = 20
  printf("test=%d,foo=%d\n",test["Hello"], foo[1,2,3,4,5,6,7,8,9])

  test["Hello"] ++
  foo[1,2,3,4,5,6,7,8,9] --
  printf("test=%d,foo=%d\n",test["Hello"], foo[1,2,3,4,5,6,7,8,9])

  delete test
  exit()
}
```


```console:実行結果
[root@server ~]# stap tp.stp
test=10,foo=20
test=11,foo=19
```

##3.3 繰り返しの使い方(foreach)


```console:サンプル
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test[1, "Hello"] = 30
  test[2, "Hello"] = 10
  test[3, "Hello"] = 20

  foreach ([a,b] in test) {
    printf("a=%d,b=%s,test=%d\n",a,b,test[a,b])
  }

  delete test
  exit()
}
```


```console:実行結果
[root@server ~]# stap tp.stp
a=1,b=Hello,test=30
a=2,b=Hello,test=10
a=3,b=Hello,test=20
```


##3.4 繰り返しの使い方(降順に表示する方法)
配列testの第1インデックスをキーに降順に連想値を表示してみます。
降順に表示するため、`[a,b]`を`[a-,b]`に変更します。

```console:サンプル（その1)
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test[1, "Hello"] = 30
  test[2, "Hello"] = 10
  test[3, "Hello"] = 20

  foreach ([a-,b] in test) {
    printf("a=%d,b=%s,test=%d\n",a,b,test[a,b])
  }

  delete test
  exit()
}
```

```console:実行結果
[root@server ~]# stap tp.stp
a=3,b=Hello,test=20
a=2,b=Hello,test=10
a=1,b=Hello,test=30
```

今度は、配列testの連想値をキーに降順に連想値を表示してみます。
降順に表示するため、`test`を`test-`に変更します。

```console:サンプル（その2)
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test[1, "Hello"] = 30
  test[2, "Hello"] = 10
  test[3, "Hello"] = 20

  foreach ([a,b] in test-) {
    printf("a=%d,b=%s,test=%d\n",a,b,test[a,b])
  }

  delete test
  exit()
}
```


```console:実行結果
[root@server ~]# stap tp.stp
a=1,b=Hello,test=30
a=3,b=Hello,test=20
a=2,b=Hello,test=10
```

##3.5 連想配列がキーを持つかどうかを判定する方法

```console:サンプル
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test[1, "Hello"] = 30
  test[2, "Hello"] = 10
  test[3, "Hello"] = 20

  if([1,"Hello"] in test)
    printf("1000\n")
  if([2,"Hello"] in test)
    printf("2000\n")
  if([3,"Hello"] in test)
    printf("3000\n")

  delete test
  exit()
}
```

```console:実行結果
[root@server ~]# stap tp.stp
1000
2000
3000
```




#4 統計集計 (Statistical Aggregates) の使い方
統計集計に値を追加するには、演算子`<<<`を使用します。


##4.1 個数を求める方法(`@count`)
配列testに10を代入してみます。

```console:スクリプト(個数が1個の場合）
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test[100] <<< 10
  printf("count=%d\n", @count(test[100]))
  delete test
  exit()
}
```

```console:実行結果
[root@server ~]# stap tp.stp
count=1
```


配列testに10と20を代入してみます。

```console:スクリプト(個数が2個の場合）
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test[100] <<< 10
  test[100] <<< 20
  printf("count=%d\n", @count(test[100]))
  delete test
  exit()
}
```

```console:実行結果
[root@server ~]# stap tp.stp
count=2
```

##4.2 最小(`@min`)、最大(`@max`)、平均(`@avg`)、合計(`@sum`)、を求める方法

```console:スクリプト
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test["hello"] <<< 10
  test["hello"] <<< 20
  test["hello"] <<< 30

  printf("count=%d,min=%d,max=%d,avg=%d,sum=%d\n",
        @count(test["hello"]),
        @min(test["hello"]),
        @max(test["hello"]),
        @avg(test["hello"]),
        @sum(test["hello"])
  )
  delete test
  exit()
}
```


```console:実行結果
[root@server ~]# stap tp.stp
count=3,min=10,max=30,avg=20,sum=60
```


#5 ヒストグラフの描き方( Histogram extractors)
ヒストグラムの書き方を以下に示します。

##5.1 hist_linearの使い方(`@hist_linear`)

```console:スクリプト
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test <<< 100
  test <<< 110
  test <<< 700
  print(@hist_linear(test, 0, 1000, 50))
  delete test
  exit()
}
```

```console:実行結果
[root@server ~]# stap tp.stp
value |-------------------------------------------------- count
    0 |                                                   0
   50 |                                                   0
  100 |@@                                                 2
  150 |                                                   0
  200 |                                                   0
      ~
  600 |                                                   0
  650 |                                                   0
  700 |@                                                  1
  750 |                                                   0
  800 |                                                   0
```

##5.2 hist_logの使い方(`@hist_log`)


```console:スクリプト
[root@server ~]# cat tp.stp
#!/usr/bin/stap
global test

probe begin
{
  test <<< 100
  test <<< 110
  test <<< 700
  print(@hist_log(test))
  delete test
  exit()
}
```


```console:実行結果
[root@server ~]# stap tp.stp
value |-------------------------------------------------- count
   16 |                                                   0
   32 |                                                   0
   64 |@@                                                 2
  128 |                                                   0
  256 |                                                   0
  512 |@                                                  1
 1024 |                                                   0
 2048 |                                                   0
```




#Z 参考情報
[3.5. SYSTEMTAP でのアレイ演算](https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/systemtap_beginners_guide/arrayoperators)
[SystemTap Language Reference](https://sourceware.org/systemtap/langref.pdf)
[mmitouの日記](https://mmitou.hatenadiary.org/entry/20120721/1342879187)
[SystemTap 3.2 SystemTap Beginners Guide](https://lrita.github.io/images/posts/systemtap/SystemTap_Beginners_Guide.pdf)