- 投稿日:2019-01-26T15:49:10+09:00
LPIC 102試験 主題106:ユーザーインターフェースとデスクトップ 関連キーワードメモ
はじめに
問題解くだけではなかなか覚えられないので、メモを残す。(5回目)
個人的にすでに知っていたものは書かない。
ゴミ記事を増やしているようで気が引けてきた。これまでに書いた記事はこちら。
LPIC 102試験 主題105:シェル、スクリプト、およびデータ管理 関連キーワードメモ
LPIC 101試験 主題104:デバイス、Linuxファイルシステム、ファイルシステム階層標準 関連キーワードメモ
LPIC 101試験 主題102:Linuxのインストールとパッケージ管理 関連キーワードメモ
LPIC 101試験 主題101:システムアーキテクチャ 関連キーワードメモキーワード
X Window System
Linuxで提供されている標準のGUI環境のこと。
Xディスプレイマネージャ
システムをランレベル5で起動した際にログイン画面を提供し、ユーザー認証やXウィンドウの起動、デスクトップ環境の起動前の準備を行う。
キーボードアクセシビリティ
障害者や高齢者向けのキーボード入力支援機能のこと。
スティッキーキー、リピートキー、スローキー、バウンスキー、トグルキー、マウスキーがある。コマンド
xhost
リモートのXクライアントによるXサーバの利用を制御するためのコマンド。
xwininfo
ウィンドウの情報を表示するコマンド。
xdpyinfo
ディスプレイの情報を表示するコマンド。
さいごに
かなり薄い情報だけになってしまった。
- 投稿日:2019-01-26T15:33:13+09:00
LPIC 102試験 主題105:シェル、スクリプト、およびデータ管理 関連キーワードメモ
はじめに
問題解くだけではなかなか覚えられないので、メモを残す。(4回目)
個人的にすでに知っていたものは書かない。これまでに書いた記事はこちら。
LPIC 101試験 主題104:デバイス、Linuxファイルシステム、ファイルシステム階層標準 関連キーワードメモ
LPIC 101試験 主題102:Linuxのインストールとパッケージ管理 関連キーワードメモ
LPIC 101試験 主題101:システムアーキテクチャ 関連キーワードメモキーワード
/etc/profile
ユーザーがログインする際に一番最初に読み込まれるbashのファイル
ユーザーログイン時のファイル読み込み順序
- /etc/profile
- .bash_profile
- .bash_profileがない場合、.bash_login
- .bash_loginがない場合、.profile
.bashrc
シェル起動時に読み込まれるファイル
.bash_logout
ログインシェルの終了時に読み込まれる。
PATH
ユーザーが入力したコマンドをシェルが検索するディレクトリを設定する環境変数。
特殊変数
$0: プログラム名 $$: 現在のシェルのプロセス番号 $#: コマンドラインに与えられた引数の個数 $?: シェルが最後に実行したコマンドの終了状態コマンド
source
指定したシェルスクリプトを実行するコマンド。
env
環境変数の情報を表示したり、その値を変更したりするコマンド。
set
定義されている変数(環境変数、シェル変数)とその値を表示する。
export
すでに設定されているシェル変数を環境変数として設定するコマンド。
unset
設定されている環境変数やシェル変数を削除するコマンド。
alias
コマンドなどの別名を登録するコマンド。
exec
新たにプロセスを生成せずに現在のプロセスに置き換えてコマンドを実行したいときに実行するコマンド。
さいごに
SQLは知っていたので飛ばしました。
- 投稿日:2019-01-26T11:16:54+09:00
GPD Pocket (初代) に Ubuntu MATE 18.10 をインストールする手順
作業の流れ
- Ubuntu インストール用 USB メモリの準備
- GPD Pocket の BIOS 設定
- インストール
1. インストール用 USB メモリの用意
USB メモリの容量は 8GB で足りる。
ここで紹介するインストール用 iso イメージの焼きこみ手段は一例である。焼ければ何でもよい。Ubuntu MATE のディスクイメージをダウンロード
https://ubuntu-mate.org/download/
>> Choose your architecture >> GPD Pocket >> 18.10 (Cosmic) >> `ubuntu-mate-18.10-desktop-amd64-gpd-pocket.iso` をダウンロードSD Card Formatter で USB メモリをフォーマット
今回は SD Card Formatter を使用した。Windows が提供する標準的な手段によるフォーマットで問題ない。
https://www.sdcard.org/jp/downloads/formatter_4/
>> SD Card Formatter をダウンロード >> インストール >> 起動 >> USB メモリを挿入する >> USB メモリが「カードの選択」に現れる(ここでは X:ドライブとする) >> USB メモリ (X:ドライブ) が「カードの選択」で選択されていることを確認する - フォーマットオプション (x) クイックフォーマット ( ) 上書きフォーマット [ ] CHSフォーマットサイズ調整 ボリュームラベルは空欄で OK >> [フォーマット] >> [はい(Y)] >> [OK]iso イメージを USB メモリに焼く
iso イメージの焼きこみに Etcher を使用した。
https://www.balena.io/etcher/>> Etcher をダウンロード >> インストール >> 起動 >> [Select image] >> ubuntu-mate-18.10-desktop-amd64-gpd-pocket.iso を選択して[開く(O)] >> [Select drive] >> フォーマットした USB メモリを選択して [Continue] >> [Flash!] >> [Continue] >> 2~3分待つ >> Flash Complete! が表示されれば成功2. BIOS 設定
ここから先は GPD Pocket で作業する。
BIOS に入る
1. 電源 OFF 状態にする 2. ブート用の USB メモリを差し込んでおく(理由:Boot Option の起動候補に USB ドライブが現れるようにするため) 3. 電源ボタンを 1 秒長押しして電源 LED が点灯したら即座に [DEL]キー押下状態を維持する 4. GPD ロゴ表示ののち BIOS が開く下記がいずれも 90 度横に回転しているのは正常なのでびっくりしないこと。
- 描画の方向
- 十字キーの方向
BIOS のバージョン確認
BIOS 情報は BIOS 起動直後に Main タブに表示されている。
本手順は BIOS がすでに 2017/08/07 最新版であることを前提としている。
BIOS Information 値 BIOS Vendor American Megatrends Core Version 5.011 Compliancy UEFI 2.4; PI 1.3 Project Version 1ARXP 0.57 x64 Build Date and Time 08/07/2017 10:05:25
BIOS が最新ではない場合・・(クリックして展開)
BIOS 更新方法は調べていません。リソースはこのあたりにあるはず。
- GPD Download Center
http://gpdjapan.com/download/
>>GPD Pocket BIOS 0807
https://mega.nz/#!RZoG2I6Y!E3tDSn2M2BNn-JxW8pX7OEo8QgxUkFLs11Uw_WiG0Wc
>>GPD Pocket BIOS 20170807.rar
USB メモリから起動する BIOS 設定
引き続き BIOS で下記を実施する。キー操作が 90度回転していることに注意。
事前に USB メモリを挿しておくこと。1. 左右キーで Boot タブに移動する 2. 上下キーで Boot Option Priorities の Boot Option #1 に移動して [Enter] 3. 1番目のブート対象として USB メモリを指定する 4. 左右キーで Save & Exit タブに移動する 5. 上下キーで Save Changes and Reset を選択して [Enter] 6. Save configuration and reset? と聞かれるので Yes で [Enter]設定値は例えば下記のようになる。本手順では #2 と #3 の値は問わない。
Boot Option Priorities 設定値 Boot Option #1 UEFI: SanDisk, Partition 1 ←USBメモリ Boot Option #2 Windows Boot Manager Boot Option #3 UEFI: Built-in EFI Shell インストール作業
BIOS から抜けると GPD Pocket がリブートし USB メモリから
GNU GRUB version 2.02
が起動する。GRUB から Ubuntu MATE の起動
GRUB は CUI で表示され、5秒間キー操作がないと一番上が自動で選択される。
この間の 5 秒はあっという間なので GRUB が見えたらすぐに上下キーを押すとよい。
ブートの選択肢 Try Ubuntu MATE without installing *Install Ubuntu MATE ←これを選ぶ OEM install (for manufacturers) Check disc for defects 2 番目の Install Ubuntu MATE を選択して [Enter] >> 画面が暗転する >> インストールが起動するまでしばらく放っておく。(画面の描画の機嫌が悪いと、GRUB の CUI が見えず選択猶予の 5 秒が経過して一番上の項目が自動でスタートする場合がある。この場合は Ubuntu の MATE デスクトップ上のアイコン
Install Ubuntu MATE 18.10
を起動すればよいのでそれ以降の手順は同じだろう)インストーラの手順
インストールを案内する GUI が起動する。ここではタッチパネルが使える。
Welcome (言語選択)
一番下にスクロールして 日本語 を選択 >> [続ける]キーボードレイアウト
外付けの Bluetooth キーボードを主に使用する前提で日本語とした。
日本語 >> 日本語 >> [続ける]無線
ネットワーク設定はインストール後に実施する方針とする。
(x) Wi-fi ネットワークに今すぐには接続しない ( ) このネットワークに接続する >> [続ける]アップデートと他のソフトウェア
インストール構成はデフォルトとする。
あらかじめどのアプリケーションをインストールしますか? (x) 通常のインストール ( ) 最小インストール その他のオプション [x] Ubuntu MATEのインストール中にアップデートをダウンロードする [ ] グラフィックスとWi-Fiハードウェアと追加のメディアフォーマットのサードパーティ製ソフトウェアをインストールする >> [続ける]インストールの種類
Windows Boot Manager は全消しの方針とする。
コンピューターには Windwows Boot Manager がインストールされています。どのようにしますか? ( ) Ubuntu MATE を Windows Boot Manager とは別にインストール (x) ディスクを削除して Ubuntu MATE をインストール [ ] 安全のため新しい Ubuntu MATE のインストールを暗号化する [ ] 新しい Ubuntu MATE のインストールに LVM を使用する ( ) それ以外 >> [インストール(I)](確認ダイアログ)ディスクに変更を書き込みますか?
以下のような念押しにひるまず先へ進む。
以下のデバイスのパーティションテーブルが変更されます: MMC/SDカード 1 (mmcblk0) 以下のパーティションは初期化されます: MMC/SDカード 1 (mmcblk0) のパーティション 1 を ESP に MMC/SDカード 1 (mmcblk0) のパーティション 2 を ext4 に >> [続ける]どこに住んでいますか?
Tokyo >> [続ける]あなたの情報を入力してください
- あなたの名前: hoge - コンピューターの名前: fuga - ユーザー名の入力: hoge - パスワードの入力: xxxxxxxx - パスワードの確認: xxxxxxxx ( ) 自動的にログインする (x) ログイン時にパスワードを要求する >> [続ける] >> インストールが始まるインストールが完了しました
しばらく待つとダイアログが出てインストール終了 >> [今すぐ再起動する] >> Ubuntu のロゴが表示される >> Please remove the installation medium, then press ENTER: >> USB メモリを抜いて [Enter]eMMC からの Ubuntu MATE 起動
Ubuntu がブートしてログオンの GUI が出たらインストール成功。
実施した環境
- GPD Pocket
- PC : Windows 10 Pro 64bit Version 1803 (Build 17134)
- USB Drive : SanDisk Cruzer Fit 8GB
- 2019/01/19 Sat
- 投稿日:2019-01-26T11:00:12+09:00
今まで実行したコマンドをスクリプト化する
PCセットアップ等で打った手番をそのままべた書きでスクリプトにしたい。
historyコマンドで確認はよくやると思うんですが、そのままファイル出力できることに気付いたので覚書history -a cmd.shcmd.shに実行手番が先頭行数なしで吐き出されます。
(今まで手動で切り貼りしてた)
- 投稿日:2019-01-26T03:07:58+09:00
テキスト好きのためのDLTログ入門
1. DLTとは?
Diagnostic Log and Traceの略。
rsyslog/jorunaldのようなログプラットフォームのこと。AUTOSARで仕様が決められていて、GENIVIがオープンソースで実装を公開している。
このことからもわかるように、車載業界でログプラットフォームとして使われている。ブロックチェーンの基礎技術、Distributed Ledger Technology; 分散型台帳管理のことだと思った方、ごめんなさい。
ちなみに2019/1/25時点でQiitaのdltタグは全部分散型台帳管理のことだった。。。2. DLTのシステム構成
githubで公開されているDLTの構成図が下記。
非常にシンプルな構成だ。
DLT Daemonが、ログ収集およびファイルシステムへの保存 or リモートサーバへの転送を担う。
各アプリケーションはDLT LibraryをリンクしてAPI経由でメッセージをDLT Daemonに転送する。
アプリ<->Deamon間の通信はいくつか選択肢があり、ビルドコンフィグで切り替えることができる。
- 名前付きパイプ(FIFO)
- Unix Domain Socket (デフォルトでは"/tmp/dlt")
- 共有メモリ(v2.18.0時点ではまだexperimentalな機能とのこと)
ちなみにrsyslog/journaldの構成図は下記がわかりやすかった。
アプリケーションはlogger(1)コマンド、もしくはlibcのsyslog(3)を使って/dev/log経由でrsyslogデーモンにデータを送付する。
journaldがいたらdaemonの標準出力等も収集してくれるらしいが、よく知らない。
(参考:【図解/CentOS7】rsyslogの仕組みと.confの設定例 〜template, property, ruleフィルタの種類〜)3. 動かしてみる
以降で取り上げる環境およびソースコードのバージョンは以下の通り。
- OS : Ubuntu 16.04 (x86_64)
- dlt-daemon: v2.18.0
- dlt-viewer: v2.18.0
3-1. dlt-daemonのビルドと実行
といっても通常のcmakeプロジェクトなので、README通りに実行すれば良い。
$ git clone --depth=1 --branch=v2.18.0 http://github.com/GENIVI/dlt-daemon.git $ sudo apt-get install cmake zlib1g-dev libdbus-glib-1-dev $ mkdir build $ cd build $ cmake .. $ make $ src/daemon/dlt-daemon3-2. ログのビューワのビルドと実行
こっちはINSTALL.txtにビルドの仕方が書かれていた。
$ git clone --depth=1 --branch=v2.18.0 http://github.com/GENIVI/dlt-viewer.git $ sudo apt install qtdeclarative5-dev $ mkdir build $ cd build $ qmake ../BuildDltViewer.pro $ make $ release/dlt_viewer下記手順でDLT daemonからのデータを受信できるようにしておく。
"Config" > "ECU Add" > "IP" > localhost > OK > "Connect All ECU"3-3. ログ出力サンプルのビルドと実行
$ cd dlt-daemon/examples/example1/ $ mkdir build $ cd build $ cmake .. $ make $ ./dlt-example13-4. ログの確認
下記のように"Hello, world"のログが確認できる。
4. 大まかなログ出力の流れ
続いて先ほどのサンプルプログラムを例に、DLTログを出力するためのAPIを見ていく。
dlt-daemon/examples/example1/example1.c#include <stdio.h> /* for printf() and fprintf() */ #include <stdlib.h> /* for atoi() and exit() */ #include <dlt.h> DLT_DECLARE_CONTEXT(con_exa1); int main() { DLT_REGISTER_APP("EXA1", "First Example"); DLT_REGISTER_CONTEXT(con_exa1, "CON", "First context"); DLT_LOG(con_exa1, DLT_LOG_INFO, DLT_STRING("Hello world!")); usleep(1000); DLT_UNREGISTER_CONTEXT(con_exa1); DLT_UNREGISTER_APP(); }順に内部を見ていく。
4-1.
DLT_DECLARE_CONTEXT(con_exa1);
dlt-daemon/include/dlt/dlt_user_macros.h/** * Create an object for a new context. * This macro has to be called first for every. * @param CONTEXT object containing information about one special logging context * @note To avoid the MISRA warning "Null statement is located close to other code or comments" * remove the semicolon when using the macro. * Example: DLT_DECLARE_CONTEXT(hContext) */ #define DLT_DECLARE_CONTEXT(CONTEXT) \ DltContext CONTEXT;なんてことはない、ただグローバル変数を定義してるだけ。
ここから、一つの実行ファイルにつき、同名のコンテキストは一箇所でだけDLT_DECLARE_CONTEXT()されなければならないことがわかる。
ちなみに他のオブジェクトファイルからこのコンテキストを使いたかったら、DLT_IMPORT_CONTEXT()を使う。4-2.
DLT_REGISTER_APP("EXA1", "First Example");
dlt-daemon/include/dlt/dlt_user_macros.h/** * Register application. * @param APPID application id with maximal four characters * @param DESCRIPTION ASCII string containing description */ #define DLT_REGISTER_APP(APPID, DESCRIPTION) do { \ (void)dlt_check_library_version(_DLT_PACKAGE_MAJOR_VERSION, _DLT_PACKAGE_MINOR_VERSION); \ (void)dlt_register_app(APPID, DESCRIPTION); } while (0)ここでは2つの関数呼び出しを行っている。
ライブラリバージョンの確認(dlt_check_library_version
)と、Application IDの登録(dlt_register_app
)だ。ライブラリバージョンの確認では、ヘッダとリンクしているライブラリとの不整合がないかを確認している。
ちなみに不整合があった場合は、DLTログを送る。
本当に問題のある不整合だったら送れない気がするが。。。ライブラリバージョンの確認をこのタイミングで行うのはなんとなく違和感がある。
普通に考えればライブラリの初期化関数でやるか、コンテキスト生成時に確認すべきだろう。
ただDLTは簡単のためかライブラリの初期化関数はない。また前述の通りDLT_DECLARE_CONTEXT()はただのグローバル変数の定義だからそんなことはできない。
コンストラクタでやる方法もなくはないが、dlt-daemonはテストを覗いてほぼCで書かれている。
そんな中で「まぁApplication IDの登録はだいたい最初に一回やるよねーだからここでも良いよねー」という妥協設計が行われたような気がする。いっそのことDLT_INIT
って名前の方がすっきりしたのに。続いて呼び出される
dlt_register_app
は、まぁApplication IDの登録するんだろうと思いきや、意外といろいろする。他のAPIも含めて最初にDLT APIが呼び出された時には、
dlt_init
が呼び出される。
dlt_init
では、ソケットを開いたり、mutexの初期化をしたり、スレッドを2個起こしたりする。そう、DLTは内部でスレッドを起こす。
2つのスレッドは、"dlt_receiver"と"dlt_segmented"。
"dlt_reciever"はログレベルなど設定変更をdaemonから受け取るためのスレッド。
"dlt_segmented"はSegmented Network Protocolをサポートするために必要らしい。
DLTではCANなどのネットワークに流れるデータをトレースログとして残す機能があるが、ネットワークデータが長すぎたときのために分割して遅れるようにしているらしい。
おそらくその長いネットワークデータを一生懸命分割して送り続けるスレッドと思われるが、使ったことないからよくわからない。それらが終わるとやっとApplication IDを登録する。
でもどこに?
DLTライブラリ内で定義しているグローバル変数dlt_user
である。
つまり、Application IDは実行ファイルに一つしか登録できない。4-3.
DLT_REGISTER_CONTEXT(con_exa1, "CON", "First context");
/** * Register context (with default log level and default trace status) * @param CONTEXT object containing information about one special logging context * @param CONTEXTID context id with maximal four characters * @param DESCRIPTION ASCII string containing description */ #define DLT_REGISTER_CONTEXT(CONTEXT, CONTEXTID, DESCRIPTION) do { \ (void)dlt_register_context(&(CONTEXT), CONTEXTID, DESCRIPTION); } while (0)
dlt_user
にContextを登録する。
もうライブラリバージョンのチェックはしない。4-4.
DLT_LOG(con_exa1, DLT_LOG_INFO, DLT_STRING("Hello world!"));
自分の素性(Application ID, Context ID)を登録したら、やっとログを出せる。
DLT_LOGは可変長引数になっている。
第一引数がContext、第二引数がログレベル、第三引数以降がログ出力したいデータ列だ。Cのprintfのようにフォーマットとデータを渡すのではなく、C++のiostreamのようにデータ列の中に必要だったら文字列を入れる感じだ。
ただ、C++のiostreamと違ってアプリが各データの型を明示的に書かなければならない。
うう、めんどくさい。。。。DLT_LOG(mycontext1,DLT_LOG_INFO, DLT_CSTRING("Frame info: ,"), DLT_CSTRING("total="),DLT_UINT16(1000),DLT_CSTRING(",") DLT_CSTRING("sync="),DLT_UINT8(0),DLT_CSTRING(",") DLT_CSTRING("reem="),DLT_UINT8(0),DLT_CSTRING(",") DLT_CSTRING("valid="),DLT_UINT16(100),DLT_CSTRING(",") DLT_CSTRING("urgent="),DLT_UINT8(1))なんでこんなことになっているのか?
それは、DLTでは転送効率を高めるため、基本もとのバイナリのままログを送信しているからだ。
バイナリをダンプしたかったらバイナリのまま送って、ビューワでデコードするという設計だ。だからたまにsprintf等でバイナリを一生懸命hexダンプしたあとDLT_LOG呼び出してるコードを見かけるが、あんなのは愚の骨頂だ。
1byteで転送できるデータを3byteにするために、CPUとメモリを無駄遣いしている。
もちろん他の用途のためにテキストにしなければならないなら仕方ないが、少なくともDLTのためには不要だ。char binary[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; char hex[1024] = {0}; char *ptr = hex; ptr += sprintf (ptr, "binary:"); for (int i = 0; i < sizeof(binary)/sizeof(binary[0]); i++) { ptr += sprintf (ptr, " %02x", binary[i]); } DLT_LOG(con_exa1, DLT_LOG_INFO, DLT_STRING(hex));下記のように、バイナリ列はそのまま送ればいい。
char binary[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; DLT_LOG(con_exa1, DLT_LOG_INFO, DLT_CSTRING("binary: "), DLT_RAW(binary, sizeof(binary)));なお、どのようなログをどのように出すべきか、についてはガイドラインがあるので目を通して置いたほうが良い。
上記のことは書いてないが色々書いてくれている。
ガイドラインの一番最初の項目が"Think first"だったのはウケた。
苦労してるんだなぁ。誰が書いたかしらんけど。話をもとに戻すと、DLT_LOGはバイナリで転送するために型情報を付与しなければならず、呼び出しが面倒だ。
ただこれはGENIVIも課題に思っているらしくC++11が使える環境で、WITH_DLT_CXX11_EXT
コンパイルオプションを有効にしたら、下記のように呼び出せる。DLT_LOG_CXX(ctx, DLT_LOG_WARN, 1.0, 65); DLT_LOG_FCN_CXX(ctx, DLT_LOG_WARN, "Test String", 145, 3.141);C++11の可変引数テンプレートを使って実装されている。
logToDlt関数をオーバーロードすることで、独自構造体の自動出力も可能だ。ちなみに
DLT_LOG_CXX
とDLT_LOG_FCN_CXX
との違いはFCNの方は__PRETTY_FUNC__
をプレフィックスにつける。だった。
どんどん話が脱線するけど、外部公開インターフェース名にCXXってつけるはあまり良くないと思う。インターフェースと実装がこれ以上なく癒着してしまっている。このAPIの機能自体はとても良いだけに残念。さて、DLT_LOGの内部はどうなっているか。
/** * Send log message with variable list of messages (intended for verbose mode) * @param CONTEXT object containing information about one special logging context * @param LOGLEVEL the log level of the log message * @param ARGS variable list of arguments * @note To avoid the MISRA warning "The comma operator has been used outside a for statement" * use a semicolon instead of a comma to separate the ARGS. * Example: DLT_LOG(hContext, DLT_LOG_INFO, DLT_STRING("Hello world"); DLT_INT(123)); */ #ifdef _MSC_VER /* DLT_LOG is not supported by MS Visual C++ */ /* use function interface instead */ #else # define DLT_LOG(CONTEXT, LOGLEVEL, ARGS ...) \ do { \ DltContextData log_local; \ int dlt_local; \ dlt_local = dlt_user_log_write_start(&CONTEXT, &log_local, LOGLEVEL); \ if (dlt_local == DLT_RETURN_TRUE) \ { \ ARGS; \ (void)dlt_user_log_write_finish(&log_local); \ } \ } while (0) #endif
log_local
なるローカル変数を定義して、それを使ってwrite_start()
->ARGS
->write_finish()
としている。
ARGS
は型情報付きのデータを送信用バッファにコピーしている。
では送信用バッファはいつ確保されるか?
これは予想通りdlt_log_write_start
でcallocしていた。
デフォルトDLT_USER_BUF_MAX_SIZE
(1390byte)のバッファがここで確保される。4-5.
DLT_UNREGISTER_CONTEXT(con_exa1);
ここまできたらもうあとは消化試合みたいなものだ。
dlt-daemon/include/dlt/dlt_user_macros.h/** * Unregister context. * @param CONTEXT object containing information about one special logging context */ #define DLT_UNREGISTER_CONTEXT(CONTEXT) do { \ (void)dlt_unregister_context(&(CONTEXT)); } while (0)名前の通りContextを使えないようにしている。
4-6.
DLT_UNREGISTER_APP()
dlt-daemon/include/dlt/dlt_user_macros.h/** * Unregister application. */ #define DLT_UNREGISTER_APP() do { \ (void)dlt_unregister_app(); } while (0)こちらも同じくApplicationの登録解除している。
あれ?threadの回収は?
探してみたらatexitハンドラで回収してた。
もうそれならOSに任せたら良くない?というかregister I/F作るときにunregister I/Fを用意するのは良い心がけだと思うけど、DLTで一旦登録解除してまた使うユースケースが想像できないな。。。
5. DLTログの構造
続いては、出力したログを見るときのためにDLTログのバイナリフォーマットを確認していく。
ちょっと意外なことにファイルヘッダはない。
メッセージごとにヘッダとペイロードがあり、メッセージの数だけそれが繰り返される。
例えば複数のDLTファイルを単純にcatしたら、それはれっきとしたDLTファイルだ。
ペイロードは、型情報→データが繰り返し入っている。5-1. メッセージヘッダの構造
メッセージヘッダは4つの構造体からなる。
この内ふたつは必須だが、残りの2つはオプションで、動作モードによってあったりなかったりする。
このあたりの構造を理解するためには、dlt_file_read_header()の実装が一番わかりやすい。
- DltStorageHeader (12byte)
- マジックワード"DLT0x01" (4byte)
- unix time (8byte)
- ECU ID (4byte)
- DltStandardHeader (4byte)
- ヘッダに関する情報のビットフィールド (1byte)
- ここを見ることで後続のオプションヘッダフィールドの有無がわかる
- メッセージID (1byte)
- ペイロード長 (2byte)
- DltStandardHeaderExtra (12byte)
- ECU ID (4byte)
- Session ID (4byte)
- システム起動からのタイムスタンプ(0.1ms単位)(4byte)
- DltExtendedHeader (10byte)
- メッセージタイプに関する情報のビットフィールド(1byte)
- ペイロードに含まれるデータの数(1byte)
- Applicatiopn ID(4byte)
- Context ID (4byte)
片方がオプションとはいえ、ECU IDが二回あるのは謎。
dlt-daemon/include/dlt/dlt_common.h/** * The structure of the DLT file storage header. This header is used before each stored DLT message. */ typedef struct { char pattern[DLT_ID_SIZE]; /**< This pattern should be DLT0x01 */ uint32_t seconds; /**< seconds since 1.1.1970 */ int32_t microseconds; /**< Microseconds */ char ecu[DLT_ID_SIZE]; /**< The ECU id is added, if it is not already in the DLT message itself */ } PACKED DltStorageHeader; /** * The structure of the DLT standard header. This header is used in each DLT message. */ typedef struct { uint8_t htyp; /**< This parameter contains several informations, see definitions below */ uint8_t mcnt; /**< The message counter is increased with each sent DLT message */ uint16_t len; /**< Length of the complete message, without storage header */ } PACKED DltStandardHeader; /** * The structure of the DLT extra header parameters. Each parameter is sent only if enabled in htyp. */ typedef struct { char ecu[DLT_ID_SIZE]; /**< ECU id */ uint32_t seid; /**< Session number */ uint32_t tmsp; /**< Timestamp since system start in 0.1 milliseconds */ } PACKED DltStandardHeaderExtra; /** * The structure of the DLT extended header. This header is only sent if enabled in htyp parameter. */ typedef struct { uint8_t msin; /**< messsage info */ uint8_t noar; /**< number of arguments */ char apid[DLT_ID_SIZE]; /**< application id */ char ctid[DLT_ID_SIZE]; /**< context id */ } PACKED DltExtendedHeader;ビットフィールドは下記にマスクが定義されている。
dlt-daemon/include/dlt/dlt_protocol.h#define DLT_HTYP_UEH 0x01 /**< use extended header */ #define DLT_HTYP_MSBF 0x02 /**< MSB first */ #define DLT_HTYP_WEID 0x04 /**< with ECU ID */ #define DLT_HTYP_WSID 0x08 /**< with session ID */ #define DLT_HTYP_WTMS 0x10 /**< with timestamp */ #define DLT_HTYP_VERS 0xe0 /**< version number, 0x1 */dlt-daemon/include/dlt/dlt_protocol.h#define DLT_MSIN_VERB 0x01 /**< verbose */ #define DLT_MSIN_MSTP 0x0e /**< message type */ #define DLT_MSIN_MTIN 0xf0 /**< message type info */5-2. メッセージペイロードの構造
前述のとおり、メッセージペイロードは、型情報とデータが交互に格納されている。
型情報にはまず4byteの種別データが格納されており、そこで「文字列」や「int32」などの型が判明する。
その型が文字列など可変長な場合はさらに2byteのデータ長、そしてその長さのデータが続く。
固定長の型の場合は、種別データの直後に実際のデータが続く。このあたりの構造はdlt_message_argument_print()の実装を見るとわかりやすい。
実は型情報のビット値によっては、さらに付加情報を入れる余地もあるが、後述のdlt-converterでも無視していたので省略。/* * Definitions of types of arguments in payload. */ #define DLT_TYPE_INFO_TYLE 0x0000000f /**< Length of standard data: 1 = 8bit, 2 = 16bit, 3 = 32 bit, 4 = 64 bit, 5 = 128 bit */ #define DLT_TYPE_INFO_BOOL 0x00000010 /**< Boolean data */ #define DLT_TYPE_INFO_SINT 0x00000020 /**< Signed integer data */ #define DLT_TYPE_INFO_UINT 0x00000040 /**< Unsigned integer data */ #define DLT_TYPE_INFO_FLOA 0x00000080 /**< Float data */ #define DLT_TYPE_INFO_ARAY 0x00000100 /**< Array of standard types */ #define DLT_TYPE_INFO_STRG 0x00000200 /**< String */ #define DLT_TYPE_INFO_RAWD 0x00000400 /**< Raw data */ #define DLT_TYPE_INFO_VARI 0x00000800 /**< Set, if additional information to a variable is available */ #define DLT_TYPE_INFO_FIXP 0x00001000 /**< Set, if quantization and offset are added */ #define DLT_TYPE_INFO_TRAI 0x00002000 /**< Set, if additional trace information is added */ #define DLT_TYPE_INFO_STRU 0x00004000 /**< Struct */ #define DLT_TYPE_INFO_SCOD 0x00038000 /**< coding of the type string: 0 = ASCII, 1 = UTF-8 */ #define DLT_TYLE_8BIT 0x00000001 #define DLT_TYLE_16BIT 0x00000002 #define DLT_TYLE_32BIT 0x00000003 #define DLT_TYLE_64BIT 0x00000004 #define DLT_TYLE_128BIT 0x00000005 #define DLT_SCOD_ASCII 0x00000000 #define DLT_SCOD_UTF8 0x00008000 #define DLT_SCOD_HEX 0x00010000 #define DLT_SCOD_BIN 0x000180006. ログの見方
さて、ログは解析時のヒントとして出力するので、いかに効率よくログから必要な情報を取り出せるか、が肝になる。
DLTではログのフォーマットがバイナリなので、見るためにはその構造をパースするツールが必要だ。
前述のdlt-viewerを含め、オフラインでDLTログを解析するためには実は3つの方法がある。
(もし他にもっといい方法があればぜひ教えてください)6-1. dlt-viewer(GUIモード)
さきほど"Hello, world"を確認した時に使ったビューワ。
一番使われていると思われる。
実際、実機を動かしつつログを確認するときなどはこれ一択と思われる。フィルタ機能やエクスポート機能等、いろいろ盛り込まれているが、分割表示ができなかったりフォントサイズの設定が弱かったりと、微妙に解析中に思考が中断されることがある。
6-2. dlt-viewer(batchモード)
上記ツールにはいくつか起動オプションが用意されていて、batchモードで起動できる。
$ ./dlt_viewer -h Usage: dlt_viewer [OPTIONS] Options: -h Print usage -p projectfile Loading project file on startup (must end with .dlp) -l logfile Loading logfile on startup (must end with .dlt) -f filterfile Loading filterfile on startup (must end with .dlf) -c logfile textfile Convert logfile file to textfile (logfile must end with .dlt) Conversion will be done in UTF8 instead of Ascii -e "plugin|command|param1|..|param<n>" Execute a plugin command with <n> parameters. -s or --silent Enable silent mode without warning message boxes.変換結果は下記のような感じ。
各メッセージが行単位で出力されるので、UNIX系コマンドラインツールとの相性がとても良い。$ ./dlt_viewer -s -c helloworld.dlt helloworld.txt $ cat helloworld.txt 0 2019/01/25 20:10:35.151695 240591.0105 0 ECU1 EXA1 CON 18747 log info verbose 1 Hello world!また、未リリースだが、dlt-viewerのmasterのHEADではcsvフォーマットでの出力も対応している。
コマンドラインから使いたいという要望が増えているのかな?
計画中のv2.19.0では入るだろうが、csvが良い人はdlt-viewerだけmasterを使えばいい。-csv Conversion will be done in CSV format
6-3. dlt-convert(CUI)
dlt-daemonリポジトリの中にひっそりと入っている。
$ cd dlt-daemon/build/src/console $ ./dlt-convert -h Usage: dlt-convert [options] [commands] file1 [file2] Read DLT files, print DLT messages as ASCII and store the messages again. Use filters to filter DLT messages. Use Ranges and Output file to cut DLT files. Use two files and Output file to join DLT files. DLT Package Version: 2.18.0 STABLE, Package Revision: v2.18.0, build on Jan 25 2019 19:08:27 -SYSTEMD -SYSTEMD_WATCHDOG -TEST -SHM Commands: -h Usage -a Print DLT file; payload as ASCII -x Print DLT file; payload as hex -m Print DLT file; payload as hex and ASCII -s Print DLT file; only headers -o filename Output messages in new DLT file Options: -v Verbose mode -c Count number of messages -f filename Enable filtering of messages -b number First messages to be handled -e number Last message to be handled -w Follow dlt file while file is increasingこちらもテキストへの変換ができる。
こちらも各メッセージが行単位で出力されるので、UNIX系コマンドラインツールとの相性がとても良い。
出力がデフォルト標準出力というところも、良い。$ ./dlt-convert -a helloworld.dlt 0 2019/01/25 20:10:35.151695 2405910105 000 ECU1 EXA1 CON- log info V 1 [Hello world!]さらに機能を削っているため、依存関係も少なく軽量なので、見つけた時は「これだ!」と思った。
が。。
大変残念な欠点があった。
1つのログの中に改行がある場合、とたんに使いづらくなってしまうのだ。具体的に出力結果を見てもらったほうが早いと思う。
複数行ログのdlt_viewerの出力$ ./dlt_viewer -s -c multiline.dlt multiline.dlt $ cat multiline.dlt 0 2019/01/25 20:39:39.349801 242335.2082 0 ECU1 EXA1 CON 19788 log info verbose 1 1st line 2nd line 3rd line複数行ログのdlt-convertの出力$ ./dlt-convert -a multiline.dlt 0 2019/01/25 20:39:39.349801 2423352082 000 ECU1 EXA1 CON- log info V 1 [1st line 2nd line 3rd line]こうなってしまうとパースしづらい。。。
もちろん[]
で囲んでくれているから、対処しようはあるのだが、微妙な感じは拭えない。意外と行志向なDLTでは、一つのメッセージ内に複数行の文字列を入れられることをあまり想定していないと思われる。
とはいえ、シリアル出力用のログと共有していたりして、実際にはそのようなケースも見受けられる。
実はこの問題は結構根が深くて、dlt-viewerのGUIモードで表示しているときにも見えなくなったりする。
せっかく種々のコストを払って出したログ、ビューワで見えてないなんてことは避けたい。
複数行のログを無くせれば良いんだろうが、実際問題ログを解析するときにそんなことを言っても仕方がない。
そういう意味でもオフラインデバッグのときは、dlt_viewerをbatchモードで動かすのが一番お気に入りだ。おまけ
今回DLTの調査をしていて、
WITH_DLT_FATAL_LOG_TRAP
というcmakeオプションを知った。
ヘルプによると、 "Set to ON to enable DLT_LOG_FATAL trap (trigger segv inside dlt-user library)"とのこと。
trapして何してくれるのかなーと実装を確認してみると。。。dlt-daemon/src/lib/dlt_user.c#ifdef DLT_FATAL_LOG_RESET_ENABLE # define DLT_LOG_FATAL_RESET_TRAP(LOGLEVEL) \ do { \ if (LOGLEVEL == DLT_LOG_FATAL) { \ int *p = NULL; \ *p = 0; \ } \ } while (0) #else /* DLT_FATAL_LOG_RESET_ENABLE */ # define DLT_LOG_FATAL_RESET_TRAP(LOGLEVEL) #endif /* DLT_FATAL_LOG_RESET_ENABLE */NULLポインタを参照しても死ねないシステムがあるとも知らないで。。。┐(´д`)┌ヤレヤレ
おわりに
なんか思ってた以上に長文になってしまった。
DLTについての日本語の情報は皆無なので、誰かの役に立てばいいな。ただ、ログのプラットフォームはシステム構成の影響を受けやすく、都合に合わせて独自拡張を加えるケースも結構あるので、そこは気をつけてください。
DLTデーモンが動いているのが別システムだからUnixDomainSocketでもFIFOでもない通信を使ったり、動的メモリ確保がダメだからcallocではなくスタックや自前アロケータから取ってきたりなど。逆に詳しい方はぜひコメント・ご指摘ください。
参考
GENIVI Diagnostic Log and Trace
AUTOSAR DLT 4.3.1 specification
github dlt-daemon
github dlt-viewer
- 投稿日:2019-01-26T01:50:38+09:00
Linuxスクリプトで文字列挿入する際、複数回実行しても1行だけ挿入できるようにする
小ネタですが、よく使うので覚書として記事化。
普通にリダイレクトすると追記され続けてイライラ
PCセットアップ等で設定ファイルの追記等を以下のように書くことがあると思います。
write_file.shecho "setting_for_something" >> some_one_setting.confこれを2度3度と実行すると、ファイルに同じ内容が追記されてうざい。
$ ./write_file.sh;./write_file.sh;./write_file.sh $ cat some_one_setting.conf setting_for_something setting_for_something setting_for_something対処: 以前の追加分を削除しとく
回避策として、sedコマンドを使って追記設定を前もって削除。
write_file.shsed -i '/setting_for_something/d' some_one_setting.conf echo "setting_for_something" >> some_one_setting.confこれなら何度実行しても1行追加になるので気持ちすっきり
$ ./write_file.sh;./write_file.sh;./write_file.sh $ cat some_one_setting.conf setting_for_something