2019/09/29

perfを用いたシステムのボトルネック解析方法

背景


システムの処理速度を改善するために、ボトルネック解析を行う必要があった。
ボトルネック解析の方法と、プロファイリングに使用したperfの使用方法に関して調査を行った。

記事の目的


perfを使用し、ボトルネック解析を行う

perf


ここでは、perfの導入方法及び使用方法について記載する。

perfとは

perf(Performance analysis tools for Linux)とはLinuxカーネル2.6.31以降で使用可能なLinuxの性能解析ツールである。
実行されているプロセス毎のCPU使用率やプロセス内で呼ばれている関数の割合などを調査できる。

利点

  • gprofのように、プログラム作成時に専用のライブラリを入れたり、コンパイル時にオプションをつける必要がない
  • フレームグラフにして、ビジュアライズできる

導入方法(Ubuntu編)

Ubuntu16.04へperfを導入する手順について記載する。
  1. OSのカーネルバージョンを調べる
  2. $ uname -a
    Linux emptySet 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
    上記の場合、カーネルバージョンは「4.4.0-116-generic」である。
  3. 上記カーネルバージョンのlinux-toolsパッケージをインストールする
  4. $ sudo apt install linux-tools-<カーネルバージョン>

使用方法

perfの使用方法は、下記の通りである。

  • コマンド内のボトルネック解析を行う場合
    1. perfで記録しながらコマンドを実行する
    2. $ perf record コマンド
      デフォルトで、記録結果ファイル「perf.data」が作成される。(-o [ファイル名.data]オプションでファイル名指定可能)
    3. perfで記録したファイルの内容を表示する(-i [ファイル名.data]オプションでファイル名指定可能)
    4. $ perf report
      例: ring_buffer_sample(l.148をbuf.setBufferSize(1000);に変更した)の場合
      Samples: 44  of event 'cpu-clock', Event count (approx.): 11000000
      Overhead  Command          Shared Object       Symbol
        11.36%  ring_buffer_sam  libc-2.23.so        [.] vfprintf
         9.09%  ring_buffer_sam  libc-2.23.so        [.] __GI___libc_write
         6.82%  ring_buffer_sam  libc-2.23.so        [.] _IO_file_xsputn@@GLIBC_2.2.5
         6.82%  ring_buffer_sam  [unknown]           [k] 0xffffffff8184e9b5
         6.82%  ring_buffer_sam  [unknown]           [k] 0xffffffff8184ef5a
         4.55%  ring_buffer_sam  ring_buffer_sample  [.] std::mutex::unlock
         4.55%  ring_buffer_sam  [unknown]           [k] 0xffffffff81508f2b
         2.27%  ring_buffer_sam  libc-2.23.so        [.] _IO_file_write@@GLIBC_2.2.5
         2.27%  ring_buffer_sam  libc-2.23.so        [.] fprintf
         2.27%  ring_buffer_sam  ring_buffer_sample  [.] RingBuffer::getBufferSize
         2.27%  ring_buffer_sam  ring_buffer_sample  [.] main
         2.27%  ring_buffer_sam  ring_buffer_sample  [.] std::mutex::lock
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff810031e8
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff81087b91
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8109b061
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8109b069
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff811c3b76
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff81213c9a
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff81214ba0
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8140cbaf
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8140cbb3
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8140dee9
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8150410d
         2.27%  ring_buffer_sam  [unknown]           [k] 0xffffffff8150420f
      For a higher level overview, try: perf report --sort comm,dso
      上記の例の場合、vfprintf(標準出力)が処理量の11%を占めているため、標準出力を減らすことが高速化に有効であることがわかる。
  • システム全体のボトルネック解析を行う場合
    1. perfで記録する
    2. $ sudo perf record -ag
    3. perfで記録したファイルの内容を表示する
    4. $ perf report
      例: ring_buffer_sample(l.148をbuf.setBufferSize(100000);に変更した)を実行した状態で取得した場合
      Samples: 181K of event 'cpu-clock', Event count (approx.): 45387500000
      Overhead  Command          Shared Object                Symbol
        59.48%  swapper          [kernel.kallsyms]            [k] 0xffffffff810656d6
         1.98%  ring_buffer_sam  [kernel.kallsyms]            [k] 0xffffffff8109b061
         0.91%  compiz           [kernel.kallsyms]            [k] 0xffffffff810031e8
         0.85%  gnome-terminal-  [kernel.kallsyms]            [k] 0xffffffff8109d9e0
         0.83%  gnome-terminal-  libglib-2.0.so.0.4800.2      [.] g_string_insert_uni
         0.80%  gnome-terminal-  [kernel.kallsyms]            [k] 0xffffffff810031e8
         0.76%  compiz           libX11.so.6.3.0              [.] 0x0000000000026e60
         0.65%  gnome-terminal-  libglib-2.0.so.0.4800.2      [.] g_utf8_get_char
         0.57%  compiz           libX11.so.6.3.0              [.] 0x0000000000026e62
         0.57%  compiz           libX11.so.6.3.0              [.] 0x0000000000026f22
         0.57%  kworker/u8:2     [kernel.kallsyms]            [k] 0xffffffff8109c9fa
         0.54%  compiz           libX11.so.6.3.0              [.] 0x0000000000026f20
         0.45%  ring_buffer_sam  [kernel.kallsyms]            [k] 0xffffffff810ac14d
         0.44%  gnome-terminal-  libglib-2.0.so.0.4800.2      [.] g_string_append_uni
         0.42%  ring_buffer_sam  libc-2.23.so                 [.] vfprintf
         0.41%  ring_buffer_sam  [kernel.kallsyms]            [k] 0xffffffff8184ef5a
         0.41%  ring_buffer_sam  [kernel.kallsyms]            [k] 0xffffffff8184e9b5
         0.41%  ring_buffer_sam  libc-2.23.so                 [.] __GI___libc_write
         0.38%  ring_buffer_sam  libc-2.23.so                 [.] _IO_file_xsputn@@GL
         0.37%  kworker/u8:1     [kernel.kallsyms]            [k] 0xffffffff8109c9fa
         0.36%  gnome-terminal-  libvte-2.91.so.0.4200.5      [.] 0x00000000000135b2
      For a higher level overview, try: perf report --sort comm,dso
      上記の例の場合、swapper(スワッピング処理)が処理量の60%を占めているため、メモリの利用率をチェックする必要がある。
  • フレームグラフでボトルネック解析結果を可視化する場合
    1. フレームグラフ作成用のスクリプトをダウンロードする
    2. $ wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/stackcollapse-perf.pl
      $ wget https://raw.githubusercontent.com/brendangregg/FlameGraph/master/flamegraph.pl
    3. perfで記録する
    4. $ sudo perf record -ag
    5. perfで記録したファイルからフレームグラフを作成する
    6. $ sudo perf script> perf_data.txt
      $ perl stackcollapse-perf.pl perf_data.txt|perl flamegraph.pl > flamegraph.svg
      例: ring_buffer_sample(l.148をbuf.setBufferSize(100000);に変更した)を実行した状態で取得した場合
      firefox ./flamegraph.svg
      flamegraph
      フレームグラフでは、関数名で左から右にソートされており、コールスタックは上に行くほど深くなる。 一番上で横幅が広い関数がCPUを長く使っているため、ボトルネックになっている。
  • timechartを取得する場合
    1. perfで記録する
    2. $ sudo perf timechart record
    3. perfで記録した結果ファイルをsvgファイル(タイムチャート)として出力する(-p [pid or プロセス名]オプションで特定のプロセスのみを抜き出せる)
    4. $ sudo perf timechart -o timechart.svg
      例: ring_buffer_sample(l.148をbuf.setBufferSize(100000);に変更した)を実行した状態で取得した場合
      $ firefox ./timechart.svg
      timechart
      上記ファイルの上半分がCPUの情報、下半分が実行されているプロセスの情報である。 各CPUの使用率のタイムチャートや、各プロセスの処理タイミングなどが分かる。 CPUに無駄なアイドル時間(Idle)がある場合や、プロセスにCPU待ち時間(Waiting CPU)や無駄な読み書き待ち時間(Blocked on IO)がある場合、処理系の最適化を図る必要がある。

その他コマンド一覧

  • perf top
  • リアルタイムに関数単位でcpu使用率を閲覧できる
    $ perf top
    ---------------------------------------------------------------------------
      PerfTop:     260 irqs/sec  kernel:61.5%  exact:  0.0% [1000Hz
    cycles],  (all, 2 CPUs)
    ---------------------------------------------------------------------------
    
                samples  pcnt function                       DSO
                _______ _____ ______________________________ __________________
    
                  80.00 23.7% read_hpet                      [kernel.kallsyms]
                  14.00  4.2% system_call                    [kernel.kallsyms]
                  14.00  4.2% __ticket_spin_lock             [kernel.kallsyms]
                  14.00  4.2% __ticket_spin_unlock           [kernel.kallsyms]
                   8.00  2.4% hpet_legacy_next_event         [kernel.kallsyms]
                   7.00  2.1% i8042_interrupt                [kernel.kallsyms]
                   7.00  2.1% strcmp                         [kernel.kallsyms]
                   6.00  1.8% _raw_spin_unlock_irqrestore    [kernel.kallsyms]
    ...
  • perf list
  • 指定可能なイベントの一覧を表示できる
    $ perf list
    
    List of pre-defined events (to be used in -e):
    
     cpu-cycles OR cycles                       [Hardware event]
     instructions                               [Hardware event]
     cache-references                           [Hardware event]
     cache-misses                               [Hardware event]
     branch-instructions OR branches            [Hardware event]
     branch-misses                              [Hardware event]
     bus-cycles                                 [Hardware event]
    ...
  • perf stat
  • 実行したコマンドのパフォーマンスカウンターが閲覧できる
    $ perf stat - make -j
    
    Performance counter stats for 'make -j':
    8117.370256  task clock ticks     #      11.281 CPU utilization factor
            678  context switches     #       0.000 M/sec
            133  CPU migrations       #       0.000 M/sec
         235724  pagefaults           #       0.029 M/sec
    24821162526  CPU cycles           #    3057.784 M/sec
    18687303457  instructions         #    2302.138 M/sec
      172158895  cache references     #      21.209 M/sec
       27075259  cache misses         #       3.335 M/sec
    Wall-clock time elapsed:   719.554352 msecs

備考

  • Symbolが16進数標記で読めない場合、addr2lineコマンドで関数名を確認できる場合もある

まとめ


  • perfを使用し、ボトルネック解析を行う方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/29: 新規作成
  2. 2020/05/04: コマンド一覧追加

2019/09/26

CUDAの導入方法(Ubuntu編)

背景


GPUで並列処理を行うために、CUDAの導入が必要となった。

記事の目的


UbuntuにCUDAを導入する

CUDA


ここでは、CUDAの導入方法について記載する。

CUDAとは

CUDAは、nvidia社が提供する並列処理ライブラリである。

利点

  • 実装に関する情報が豊富である
  • TensorflowやOpenCVなど、機械学習や画像処理に関するツールはが基本的に対応している
  • C言語ライクな記述でプログラミングできる

導入方法

OSやマシンにあったCUDAのインストーラ方法は、公式ページから調べることができる。
今回は、Ubuntu16.04へCUDAを導入する手順について記載する。
  1. CUDAのアーカイブをダウンロードし、aptに登録する
  2. $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-ubuntu1604.pin
    $ sudo mv cuda-ubuntu1604.pin /etc/apt/preferences.d/cuda-repository-pin-600
  3. OSやマシンにあった承認キーをダウンロードページから探す(今回は、/ubuntu1604/x86_64/7fa2af80.pub)
  4. 承認キーをダウンロードする
  5. $ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
  6. CUDAのレポジトリを登録する
  7. $ sudo add-apt-repository "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/ /"
  8. CUDAをインストールする
  9. $ sudo apt-get update
    $ sudo apt-get -y install cuda
  10. PCを再起動する
  11. $ sudo reboot

CUDAのサンプルプログラム

CUDAのサンプルプログラムのmake方法は、下記の通りである。
$ /usr/local/cuda-10.1/bin/cuda-install-samples-10.1.sh ~
$ cd ~/NVIDIA_CUDA-10.1_Samples
$ make
上記の場合、~/NVIDIA_CUDA-10.1_Samples内にサンプルプログラムの実行ファイルが作成される。

アンインストール方法

CUDAのアンインストール方法について記載する。
$ sudo apt remove cuda-10-1
$ sudo apt autoremove
$ sudo apt remove libcudnn7 libcudnn7-dev libcudnn7-doc
$ rm -rfv ~/NVIDIA_CUDA-10.1_Samples/

まとめ


  • UbuntuにCUDAを導入する方法ついて調査、記載した

参考文献



変更履歴


  1. 2019/09/26: 新規作成

2019/09/24

NvidiaGPUの温度を取得するスクリプトを作成(Linux)

背景


GPUを搭載したPCでのソフトウェアのヒートランを行った際、GPUの温度をモニタリングする必要があったため、スクリプトを作成した。

記事の目的


NvidiaGPUの温度を取得するスクリプトを作成する

nvidia-smi


ここでは、NvidiaGPUの温度を取得するスクリプトの記述方法について記載する。

スクリプトの作成方法

スクリプトの作成方法は以下の通りである。
  1. スクリプトgpu_temperature_monitoring.shを作成する
  2. #!/bin/bash
    echo "Start to record GPU temperature!"
    DIR_NAME=~/gpu_temperature_log/`date +%Y%m%d`
    LOG_NAME=./`date +%Y%m%d_%H%M%S`_gpu_temperature.log
    mkdir -p $DIR_NAME
    cd $DIR_NAME
    echo "Date,Temperature[C]" >> $LOG_NAME
    while :
    do
        nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader,nounits | awk '{ "date +\"%Y/%m/%d %T\"" | getline var; print var "," $0 }' >> $LOG_NAME
        sleep 1
    done
    exit 0
  3. スクリプトに実行権限を与える
  4. $ chmod 777 ./gpu_temperature_monitoring.sh 

スクリプトの実行方法

実行方法は以下の通りである。
$ ./gpu_temperature_monitoring.sh
Start to record GPU temperature!
~/gpu_temperature_monitoring_log/日付/にGPU温度のログが作成される
$ less ./gpu_temperature_log/20190923/20190923_234227_gpu_temperature.log
Date,Temperature[C]
2019/09/23 23:42:27,51
2019/09/23 23:42:28,51
2019/09/23 23:42:29,51
2019/09/23 23:42:30,52

まとめ


  • NvidiaGPUの温度を取得するスクリプトを作成した

参考文献



変更履歴


  1. 2019/09/24: 新規作成

2019/09/21

UbuntuへのJupyter notebookの導入方法

背景


仕事で、データ解析や機械学習の学習環境用に可読性の高いプログラミング環境を構築する必要があった。


記事の目的


Jupyter notebookを導入する

Jupyterの導入


ここでは、Jupyter notebookの導入方法と使用方法について記載する。

Jupyter notebookとは

Jupyter notebook
Jupyter notebookとはブラウザ上で実行し、実行結果を記録しながらプログラミングを進めるためのツールである。データ分析の現場や、研究機関などでも頻繁に使われている。

利点

  • PythonやRubyなど多様な言語に対応している
  • markdownでメモ、コメントが記述できる
  • グラフや表を表示できる

導入方法

Jupyter notebookの導入手順は、下記の通りである。

Pyenv+Anacondaの場合

  1. Pyenvを導入する
  2. 最新のAnaconda (Python3系) のバージョンを確認する
  3. $ pyenv install -l | grep anaconda3
      anaconda3-2.0.0
      anaconda3-2.0.1
      anaconda3-2.1.0
      anaconda3-2.2.0
      anaconda3-2.3.0
      anaconda3-2.4.0
      anaconda3-2.4.1
      anaconda3-2.5.0
      anaconda3-4.0.0
      anaconda3-4.1.0
  4. 最新のAnaconda (ここでは4.1.0) をインストールし、デフォルトとして設定する
  5. $ pyenv install anaconda3-4.1.0
    $ pyenv global anaconda3-4.1.0
    $ echo 'export PATH="$PYENV_ROOT/versions/anaconda3-4.1.0/bin:$PATH"' >> ~/.bashrc
    $ echo 'alias activate="source $PYENV_ROOT/versions/anaconda3-4.1.0/bin/activate"' >> ~/.bashrc
    $ source ~/.bashrc
  6. Pythonの環境を確認する
  7. $ python --version
    Python 3.5.1 :: Anaconda 4.1.0 (64-bit)

pipで直接インストールする場合

  1. 必要なパッケージをインストールする
  2. $ pip install numpy
    $ pip install scipy
    $ pip install matplotlib
    $ pip install Pillow
    $ pip install ipython[all]
  3. Jupyter Notebookをインストールする
  4. $ pip install jupyter

使用方法

Jupyter notebookの使用手順は、下記の通りである。
  1. ターミナル上で下記を実行する
  2. $ jupyter notebook

備考

  • matplotlibのグラフをインラインで表示するためには、はじめに下記を記述する
  • %matplotlib inline
    import matplotlib.pyplot as plt


まとめ


  • Jupyterの導入方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/21: 新規作成

2019/09/19

黒画面でカーソルが点滅し、OSが起動しない場合の対処方法

背景


クローンしたUbuntuをIntel NUC上で起動させようとした際に、黒画面でカーソルが点滅した状態となり、OSが起動しない現象が生じた。

記事の目的


Boot Repairでブートローダーの問題を修復し、OSが起動できるようにする

Boot Repair


ここでは、Boot Repairでブートローダーの問題を修復する方法について記載する。

修復手順

Boot Repairでブートローダーの問題を修復する方法は以下の通りである。
  1. Ubuntuをメディアブートで起動する(言語は必ず「英語」を選択。日本語を選択すると、後述のBoot Repairがインストールできない場合がある)
  2. Boot Repairをインストールする
  3. [Ctrl]+[ALT]+[t]キーの同時押しでターミナルを立ち上げ、下記を入力する。
    $ sudo add-apt-repository ppa:yannubuntu/boot-repair
    $ sudo apt update
    $ sudo apt install -y boot-repair
  4. デスクトップ画面左上の「Activities」をクリックし、「Boot Repair」を検索、実行する
  5. 「Recommended repair」ボタンをクリックし、修復を実行する
  6. Boot Repair

まとめ


  • Boot Repairでブートローダーの問題を修正する方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/19: 新規作成
  2. 2020/05/07: 修復手順に画像追加

2019/09/17

Xvfbで仮想ディスプレイを作成する方法

背景


映像出力の無い状況下で、WebRTCといった映像系のWebアプリケーションを動作させようとすると、正常に動作しない場合があったため、仮想ディスプレイを作成する方法を調査した。

記事の目的


Xvfbで仮想ディスプレイを作成し、アプリケーションの映像出力を仮想ディスプレイに設定する

Xvfb


ここでは、Xvfbで仮想ディスプレイを作成する方法について記載する。

Xvfbの導入方法

Xvfbの導入方法は以下の通りである。
  1. xvfbをapt installする
  2. $ sudo apt install xvfb 
  3. Ubuntu起動時にxvfbを自動起動するように設定する
  4. $ sudo nano /etc/rc.local
    ...
    Xvfb :99 -ac -screen 0 1024x768x16 &
    exit 0
    
    上記の設定で、ディスプレイ番号99が仮想ディスプレイとして設定される。
  5. PCを再起動する

仮想ディスプレイを映像出力先としてアプリケーションを実行する方法

実行方法は以下の通りである。(例としてFirefoxを使用)
$ env DISPLAY=:99.0 firefox

備考

  • 描画処理は動いているため、その分CPU(GPU)リソースは消費される

まとめ


  • Xvfbで仮想ディスプレイを作成した

参考文献



変更履歴


  1. 2019/09/17: 新規作成

2019/09/14

Kerasの導入方法

背景


仕事で、ディープラーニングのモデルを学習させ、結果をエッジに組み込む必要が出てきた。ここでは、モデルの学習環境構築の方法について記載する。

記事の目的


Kerasを導入する

Kerasの導入


ここでは、Kerasの導入方法と使用方法について記載する。

Kerasとは

Keras
KerasはPythonで書かれた、TensorFlowまたはCNTK,Theano上で実行可能な高水準のニューラルネットワークライブラリである。

利点

  • pipで簡単に導入できる
  • Tensorflowと比べて学習コストが小さい
  • 学習モデルをTensorflowなどに移植可能

導入方法

Kerasの導入手順は、下記の通りである。(GPU対応版)
  1. CUDAをインストールする(現時点では、CUDA10.0)
  2. cuDNNをインストールする(要ユーザ登録)
  3. Python 3.6をインストールする
  4. tensorflow-gpuをインストールする
  5. $ pip install tensorflow-gpu
  6. Kerasをインストールする
  7. $ pip install keras
GPU版のTensorflowが動作しているかは、下記で確認できる。
$ python
>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()
...
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: XXXXXXXXX
locality {
}
incarnation: 10479716717325198190
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: XXXXXXXXXX
locality {
  bus_id: 1
  links {
  }
}
incarnation: 3656906984786649108
physical_device_desc: "device: 0, name: GeForce GTX XXX XGB, pci bus id: 0000:01:00.0, compute capability: X.X"
]
CPU版の場合、GPUに関する記述がない。

使用方法

Kerasの使用手順は、下記の通りである。
$ python
>>> import keras
Using TensorFlow backend.

まとめ


  • Kerasの導入方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/14: 新規作成

2019/09/12

google testでrosパッケージの単体テストを行う方法

背景


rosパッケージを作成する際に、品質担保のために通常単体テストを行う。
googleテストの使用方法やCMakeListsの書き方を備忘録として記載する。

記事の目的


google testでrosパッケージの単体テストを行う

google test


google testでrosパッケージの単体テストを行う方法について記載する。

google testとは

google testは、google社が提供するオープンソースのテストフレームワークである。

特徴

  • マクロを使用してテストケースを容易に記述できる
  • rosの標準のテストフレームワークである

テンプレートのパッケージ構成

google testでrosパッケージの単体テストを行うrosパッケージの構成を記載する。
$ tree
.
├── CMakeLists.txt -> /opt/ros/kinetic/share/catkin/cmake/toplevel.cmake
└── sample_pkg                # google testでテストを行うサンプルパッケージ
     ├── CMakeLists.txt       # CMakeListsファイル
     ├── include
     │   └── sample_pkg
     │       └── sample.hpp  # headerファイル
     ├── launch
     │   └── sample.launch   #(オプション)launchファイル
     ├── package.xml          # Packageファイル
     ├── src
     │   ├── main.cpp        # ソースファイル(main関数のみ)
     │   └── sample.cpp      # ソースファイル(その他関数, クラス)
     └── test
          └── utest.cpp       # 単体テストファイル

テンプレートのheaderファイルとソースファイル

テンプレートのheaderファイルとソースファイルを記載する。
// sample.hpp
#include <ros/ros.h>

namespace sample
{
    int  func1(int a, int b);
    bool func2(int c, int d);
    class class3
    {
        public:
            class3();
            ~class3();
            void func4(int e);
            void func5(int &f);
        protected:
            int internal_variable_ = 0;
    };
};

// sample.cpp
#include <sample_pkg/sample.hpp>

int sample::func1(int a, int b)
{
    return a+b;
}

bool sample::func2(int a, int b)
{
    if (a == b)
    {
        return true;
    }
    return false;
}

sample::class3::class3()
{}

sample::class3::~class3()
{}

void sample::class3::func4(int e)
{
    internal_variable_ = e;
}

void sample::class3::func5(int &f)
{
    f = internal_variable_;
}

// main.cpp
#include <sample_pkg/sample.hpp>

int main(int argc, char **argv){
    int A = sample::func1(1,3);
    
    return A;
}


テンプレートの単体テストファイル

テンプレートの単体テストファイルを記載する。
// utest.cpp
#include <sample_pkg/sample.hpp>
#include <gtest/gtest.h>

// sample::func1のテスト
TEST(Factorial1Test, func1Test)
{
    int A = sample::func1(1,2);
    EXPECT_EQ(3, A); // A == 3 かチェック
}

// sample::func2のテスト
TEST(Factorial2Test, func2Test)
{
    EXPECT_TRUE(sample::func2(1,1)); // a == bの時、戻り値がtrueかチェック
    EXPECT_FALSE(sample::func2(1,2)); // a != bの時、戻り値がfalseかチェック
}

// sample::class3のテスト
TEST(Class3Test, func4_5Test1)
{
    sample::class3 c;
    int a;
    c.func5(a);
    EXPECT_EQ(0, a); // 初期状態でc.func5(a)のa==0かチェック
}

// sample::class3のテスト
TEST(Class3Test, func4_5Test2)
{
    sample::class3 c;
    int a = 4;
    int b = 0;
    c.func4(a);
    c.func5(b);
    EXPECT_EQ(a, b); // c.func4(a)を行った後、c.func5(b)のb==aかチェック
}

// Run all the tests that were declared with TEST()
int main(int argc, char **argv){
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

テンプレートのCMakeListsファイルとPackageファイル

テンプレートのCMakeListsファイルとPackageファイルを記載する。
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(sample_pkg)

## Compile as C++11, supported in ROS Kinetic and newer
add_compile_options(-std=c++11 -Wall -g -O3)

## Find catkin macros and libraries
find_package(catkin REQUIRED COMPONENTS
             roscpp
)

###################################
## catkin specific configuration ##
###################################

## Declare things to be passed to dependent projects
catkin_package(
   INCLUDE_DIRS include
   LIBRARIES sample_pkg
)

###########
## Build ##
###########

## Specify additional locations of header files
include_directories(include
                    /usr/local/include
                    ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
add_library(${PROJECT_NAME}_lib
            src/sample.cpp
)

## Declare a C++ executable
add_executable(${PROJECT_NAME}
               src/main.cpp
)

## Specify libraries to link a library or executable target against
target_link_libraries(${PROJECT_NAME}_lib
                      ${catkin_LIBRARIES}
)

target_link_libraries(${PROJECT_NAME}
                      ${PROJECT_NAME}_lib
                      ${catkin_LIBRARIES}
)

#############
## Install ##
#############

## Mark executables and/or libraries for installation
install(TARGETS ${PROJECT_NAME}
        ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
        LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
        RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

## Mark cpp header files for installation
install(DIRECTORY include/${PROJECT_NAME}/
        DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
        FILES_MATCHING PATTERN "*.hpp"
)

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
catkin_add_gtest(${PROJECT_NAME}_test test/utest.cpp)
target_link_libraries(${PROJECT_NAME}_test
                      ${PROJECT_NAME}_lib
                      ${catkin_LIBRARIES}
)

<!-- package.xml -->
<?xml version="1.0"?>
<package format="2">
  <name>sample_pkg</name>
  <version>0.0.0</version>
  <description>The sample package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <maintainer email="xxx@yyy.zz">EmptySet</maintainer>

  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>MIT</license>

  <!-- Url tags are optional, but multiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <url type="website">https://ittechnicalmemos.blogspot.com</url>

  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <author email="xxx@yyy.zz">EmptySet</author>

  <!-- The *depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <exec_depend>roscpp</exec_depend>
</package>

テンプレートの単体テスト実行方法

テンプレートの単体テスト実行方法及び結果を記載する。
$ catkin_make run_tests
...
- run_tests.py: execute commands
  /home/emptySet/work_space/devel/lib/sample_pkg/sample_pkg_test --gtest_output=xml:/home/emptySet/work_space/build/test_results/sample_pkg/gtest-sample_pkg_test.xml
[==========] Running 4 tests from 3 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Factorial1Test
[ RUN      ] Factorial1Test.func1Test
[       OK ] Factorial1Test.func1Test (0 ms)
[----------] 1 test from Factorial1Test (0 ms total)

[----------] 1 test from Factorial2Test
[ RUN      ] Factorial2Test.func2Test
[       OK ] Factorial2Test.func2Test (0 ms)
[----------] 1 test from Factorial2Test (0 ms total)

[----------] 2 tests from Class3Test
[ RUN      ] Class3Test.func4_5Test1
[       OK ] Class3Test.func4_5Test1 (0 ms)
[ RUN      ] Class3Test.func4_5Test2
[       OK ] Class3Test.func4_5Test2 (0 ms)
[----------] 2 tests from Class3Test (0 ms total)

[----------] Global test environment tear-down
[==========] 4 tests from 3 test cases ran. (0 ms total)
[  PASSED  ] 4 tests.
-- run_tests.py: verify result "/home/emptySet/work_space/build/test_results/sample_pkg/gtest-sample_pkg_test.xml"
...

テスト結果がNGの場合は、下記のように表示される。
$ catkin_make run_tests
...
/home/emptySet/work_space/src/sample_pkg/test/utest.cpp:31: Failure
Value of: a
  Actual: 4
Expected: 2
[  FAILED  ] Class3Test.func4_5Test2 (0 ms)
...
[  FAILED  ] 1 test, listed below:
[  FAILED  ] Class3Test.func4_5Test2

 1 FAILED TEST
...

備考

  • google testのマクロはここを参照。

まとめ


  • google testでrosパッケージの単体テストを行う方法を調査、記載した

参考文献



変更履歴


  1. 2019/09/12: 新規作成

2019/09/09

Visual Studio Codeのプラグイン導入

背景


仕事で、ドキュメント作成やプログラミング作成にVisual Studio Code (以下vscode)を使用している。
個人的によく使うプラグインについて記載する。

記事の目的


ドキュメント作成やプログラミング作成でよく使うプラグインを記載する

Visual Studio Codeのプラグイン導入


ここでは、vscodeのプラグイン導入方法と使用方法について記載する。

Code Spell Checker

Code Spell Checker
Code Spell Checkerは、英語のスペルチェックを自動で行ってくれるプラグインである。

利点
  • プログラミングの際のクラス・関数・変数名、文言などのスペルミスを未然に防ぐことができる
  • スペースで区切られていない単語(ThisIsAPen)のスペルチェックも可能

使用方法
  • 自動的に、スペルミスした単語が強調される

Markdown Preview Enhanced


Markdown Preview Enhancedは、マークダウン形式で記載したドキュメントをプレビューできるプラグインである。

利点
  • マークダウン形式のドキュメントを変更すると、プレビュー画面へ変更が即座に反映される
  • htmlやpdf形式などで出力することができる

使用方法
  • ctr+shift+v: プレビュー画面表示
  • プレビュー画面右クリック: 保存タブ表示(html, pdfなど)

PlantUML

PlantUML

PlantUMLは、UML形式で記述した図をプレビューできるプラグインである。


利点
  • UML形式形式の図を変更すると、プレビュー画面へ変更が即座に反映される
  • 画像形式などで出力することができる

使用方法
  • Alt+D: プレビュー画面表示

まとめ


  • Visual Studio Codeのプラグインについて調査、記載した

参考文献



変更履歴


  1. 2019/09/10: 新規作成

2019/09/07

Visual Studio Code導入手順

背景


仕事で、ドキュメント作成やプログラミング作成にVisual Studio Code (以下vscode)を使用している。
そこで、Windows及びUbuntuへのvscodeの導入方法と、初期設定について記載する。

記事の目的


Visual Studio Codeを導入する

Visual Studio Codeの導入


ここでは、vscodeの導入方法と使用方法について記載する。

vscodeとは

Visual Studio Code

vscodeは、Microsoftが開発したオープンソースのエディターである。

利点

  • Windows, MacOS, Linuxで動作し、操作方法がほぼ共通である
  • 多くのプラグインが存在し、ワンクリックで導入できる
  • 比較的軽量で、使用中にストレスを感じることがない
  • デフォルトでgitと連携しており、commit, push, pull, diffがワンクリックで行え、バージョン管理や変更点確認が容易にできる

導入方法(Windows編)

Windowsへのvscodeの導入手順は、下記の通りである。
  1. 公式サイトから、インストーラをダウンロードする
  2. インストーラを起動し、インストール行う
  3. vscodeのインストーラ(Windows)

導入方法(Ubuntu編)

Ubuntuへのvscodeの導入手順は、下記の通りである。
  1. vscodeのインストールに必要なプログラムをインストールする
  2. $ sudo apt install curl apt-transport-https
  3. aptのリポジトリで使用するマイクロソフトのキーをダウンロードする
  4. $ curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
  5. ダウンロードしてきたキーを/etc/apt/trusted.gpg.d/にコピーする
  6. $ sudo install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
  7. リポジトリを登録する
  8. $ sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
  9. インストールする
  10. $ sudo apt update
    $ sudo apt install code
  11. 起動する
  12. $ code .

初期設定方法

初期設定方法は、下記の通りである。
  • 日本語化
    1. vscodeを開く
    2. 「view」を選択する
    3. 「comanndo palette」を選択する
    4. 「configure display language」を選択する
    5. 「locale」を「en」から「ja」に書き換える。
    6. "locale":"ja" // Changes will not take effect until VS Code has been restarted.
    7. 左側にある四角いアイコン(extension)をクリックする
    8. 検索窓から「Japanese Language Pack for Visual Studio Code」を検索しインストールする
    9. vscodeを再起動する
  • 全角・半角スペースの表示設定
    1. vscodeを開く
    2. 「code」から「preferences」>「Settings」を選択する
    3. 「Editor:Render Whitespace」の項目を「all」に変更する

アンインストール方法(Ubuntu編)

Ubuntuからのvscodeのアンインストールは、下記の通りである。
  1. vscodeのインストールに使用したプログラムとvscodeをアンインストールする
  2. $ sudo apt remove apt-transport-https curl code
    $ sudo apt autoremove
  3. リポジトリを削除する
  4. $ sudo rm /etc/apt/sources.list.d/vscode.list
  5. ダウンロードしてきたキーを削除する
  6. $ sudo rm /etc/apt/trusted.gpg.d/microsoft.gpg

まとめ


  • Visual Studio Codeの導入方法について調査、記載した

参考文献



変更履歴


  1. 2019/09/07: 新規作成

2019/09/05

実行中のrosノードを全て終了させるスクリプトの作成方法

背景


実行している複数rosノードをCtr+Cでkillする際、時々一部のノードが終了されずにバックグラウンドで実行される場合があった。このような場合、次にrosノードを起動した際に不具合が生じる可能性がある。そこで、実行中のrosノードを全てkillするスクリプトを作成した。

記事の目的


実行中のrosノードを全てkillするスクリプトを作成する

kill_all_rosnode.sh


ここでは、実行中のrosノードを全てkillするスクリプトの記述方法について記載する。

スクリプトの作成方法

スクリプトの作成方法は以下の通りである。
  1. スクリプトkill_all_rosnode.shを作成する
  2. #!/bin/bash
    echo "Kill all ros nodes!"
    ps aux | grep ros | grep -v grep | awk '{ print "kill -9", $2 }' | sh
    exit 0
  3. スクリプトに実行権限を与える
  4. $ chmod +x ./kill_all_rosnode.sh 

スクリプトの実行方法

実行方法は以下の通りである。
$ ./kill_all_rosnode.sh
Kill all ros nodes!

まとめ


  • 実行中のrosノードを全てkillするスクリプトを作成した

参考文献



変更履歴


  1. 2019/09/05: 新規作成

2019/09/03

USBポートでデバイス名を固定する方法(Linux)

背景


同一デバイスを複数接続し、それらのデバイス名を固定する必要があった。デバイスが同一の為、udevのベンダーIDとプロダクトIDを利用してデバイス名を固定することができなかった。そこで、接続したUSBポートを用いた識別を行った。

記事の目的


同一デバイスを、接続したUSBポートの場所で識別し、デバイス名を固定する

接続したUSBポートでデバイスを識別


ここでは、ベンダーIDとプロダクトIDを利用した通常の識別方法と、接続したUSBポートの場所でデバイスを識別する方法についてそれぞれ記載する。

ベンダーIDとプロダクトIDを利用した識別方法

ベンダーIDとプロダクトIDを利用したデバイスの識別及びデバイス名の固定方法は下記の通りである。

  1. デバイスのベンダーIDとプロダクトIDの検索
  2. lsusbを用いて、デバイスのベンダーIDとプロダクトIDを検索する
    $ lsusb
        Bus 002 Device 002: ID idVendor:idProduct Intel Corp.
        Bus 003 Device 016: ID 054c:06c1 Sony Corp.
            :
        Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
  3. udevルールの作成
  4. /etc/udev/rules.dにルールファイルを作成する
    $ sudo nano /etc/udev/rules.d/80-nfc.rules
        SUBSYSTEM=="usb", ATTR{idVendor}=="054c", ATTR{idProduct}=="06c1", SYMLINK+="nfc"
    
    これで、Bus 003 Device 016: ID 054c:06c1 Sony Corp./dev/nfcとして毎回認識されるようになった。

接続したUSBポートの場所を利用した識別方法

上記方法では、同一のデバイスの場合、識別できない。そこで、接続したUSBポートで識別する。
  1. デバイスが接続されたUSBポートを探索する
  2. /dev/serial/by-path/以下に、接続したデバイスがUSBポートの位置で識別され、配置されている
    $ ls /dev/serial/by-path/
    pci-0000:00:1a.0-usb-0:1:1.0-port0
    pci-0000:00:1d.0-usb-0:2:1.0-port0
  3. シンボリックリンクを作成し、デバイス名を固定する
  4. /dev/にシンボリックリンクを作成することで、そのUSBポートに接続したデバイスのデバイス名が固定される
    $ sudo ln -s /dev/serial/by-path/pci-0000:00:1a.0-usb-0:1:1.0-port0 /dev/nfc
    これで、USBポートpci-0000:00:1a.0-usb-0:1:1.0-port0に接続されたデバイスが/dev/nfcとして毎回認識されるようになった。

備考

  • /dev/serial/by-id/以下に、接続したデバイスがシリアル番号で識別され、配置されている
  • /dev/disk/以下に、接続したディスクが/dev/serial/と同様の形でに配置されている

まとめ


  • 同一デバイスを、接続したUSBポートの場所で識別し、デバイス名を固定する方法について記載した

参考文献



変更履歴


  1. 2019/09/03: 新規作成

2019/09/01

Doxygen対応のコメント記載方法(C, C++)

背景


仕事でプログラミングする際に、関数に説明を追加する場合が多いため、Doxygen対応のコメント方法について記載する。

記事の目的


Doxygen対応のコメント記載方法について記す

Doxygen対応のコメント


ここでは、Doxygen対応のコメント記載方法について記す。

ファイルへのコメント

ファイルの先頭に、ファイル内のプログラムの説明を記載する。
/**
 * @file ファイル名.h
 * @brief 簡単な説明
 * @author 書いた人
 * @date 作成日付
 */

関数へのコメント

関数毎に、関数の説明を記載する。
/**
 * @fn func1(int, int)
 * ここに関数の説明を書く
 * @brief 要約説明
 * @param arg1 引数の説明
 * @param arg2 引数の説明
 * @return 戻り値の説明
 * @sa 参照すべき関数を書けばリンクが貼れる
 * @detail 詳細な説明
 */
void func1(int arg1, int arg2)
{}

変数へのコメント

変数毎に、変数の説明を記載する。
//! 変数へのコメント
int a = 0;

マクロへのコメント

マクロ毎に、マクロの説明を記載する。
/** @def
 * マクロのコメント
 */
#define MAX_XXX 256

列挙体へのコメント

列挙体毎に、列挙体の説明を記載する。
/**
 * @enum Enum
 * 列挙体の説明
 */
enum Enum {
    //! 列挙体の各要素の説明
    EnumItem1 = 0x00,

    //! 列挙体の各要素の説明
    EnumItem2 = 0x01
};

構造体へのコメント

構造体体毎に、構造体の説明を記載する。
/**
 * @struct     構造体名
 * @brief      構造体の説明
**/
struct Struct{
    //! 構造体の各要素の説明
    StructItem1 = 0x00,

    //! 構造体の各要素の説明
    StructItem2 = 0x01
};

クラスへのコメント

クラス毎に、クラスの説明を記載する。
int global_var1
int global_var2
int global_var3
int global_var4

/*! @class Class1
    @brief  クラスの説明
*/
class Class1 {
public:
    /*! メンバ1の説明 */
    int member1;

    int member2; /*!< メンバ2の説明を横につける */

    /*! メソッド1の説明 */
    int method1(int var1, int var2);

    /*! メソッド2の説明。詳細説明
        @param[out]     var1    var1の説明
        @param[in]      var2    var2の説明
        @param[in,out]  var3    var3の説明
        @par            Refer
        - 参照するグローバル変数 global_var1
        - 参照するグローバル変数 global_var2
        @par            Modify
        - 変更するグローバル変数 global_var3
        - 変更するグローバル変数 global_var4
        @return         成功 0, 失敗 0 以外 など
        @exception      例外。不要であればnoneを記述
    */
    int method2(int var1, int var2, int var3) {
    ...
    }
};

まとめ


  • Doxygen対応のコメント記載方法を調査、記載した

参考文献



変更履歴


  1. 2019/09/01: 新規作成

MQTTの導入

背景 IoTデバイスの接続環境構築のため、MQTT(mosquitto)の導入を行った。 記事の目的 MQTT(mosquitto)をUbuntuに導入する mosquitto ここではmosquittoについて記載する。 MQTT MQTT(Message Qu...