VSCodeでarduino開発環境構築メモ
概要
VSCodeでArduino開発環境を構築するまでのメモ。 Windows10 20H2 上で環境構築した。*1
VSCodeで開発環境を構築できるようなので、VSCode入門もかねて環境構築した。
目次
ダウンロード
Visual Studio Code本体+ArduinoSDK+VSCodeのArduino拡張を入れる。 *2
VSCode
Download Visual Studio Code - Mac, Linux, Windows
Arduino SDK
Windows app 形式でなく、旧形式でインストールした。 途中でドライバ等のインストールが促されるのでOKを選択する。
https://www.arduino.cc/en/software
VSCodeのarduino拡張
VSCodeを起動し、左メニューのExtensions を選択し、検索窓からArduinoを検索する。 Microsoft製の拡張を選択し、installボタンを押下する。
設定
VSCodeのarduino拡張設定
Arduino:Path のみ設定を行った。Program Files以下のパスを設定する。
自分の場合は、C:\Program Files (x86)\Arduino
日本語文字化け対策
下記を参考にした。 ユーザーディレクトリ以下の .vscode\extensions\vsciot-vscode.vscode-arduino-0.3.4\out\src\common\utils.js を編集し、下記をコメントアウト。
/* if (os.platform() === "win32") { try { const chcp = childProcess.execSync("chcp.com"); codepage = chcp.toString().split(":").pop().trim(); } catch (error) { outputChannel_1.arduinoChannel.warning(`Defaulting to code page 850 because chcp.com failed.\ \rEnsure your path includes %SystemRoot%\\system32\r${error.message}`); codepage = "850"; } } */
ArduinoIDEをアップデートしたらVSCodeのArduino出力で文字化け - Qiita
サンプルプログラムの動作確認
VSCodeのコマンドパレット経由でArduinoの拡張を使用し、通信等を行うようだ。 Ctrl+Shift+Pでコマンドパレットを開き、Arduinoを入力すると候補が表示される。
サンプルプログラムを開く (Arduino:Examples)
コマンドパレットからArduino:Examplesを選択し、適当なサンプルを開く。
試しにBuilt-in Examples\01.Basics\Blink を開く。
ボード等の設定
Blink.ino を開くと画面下部の青いバー(Status Bar)にいろいろと表示されるので、こちらからBoard TypeとシリアルCOMポートの設定を行う。
<Select Board Type>
を押下しSelected Board:
から 焼きこみ先のボードを選択する (今回はArduino Uno)COM1
を押下し、適切なCOMポートを選択する。(環境によると思うので適宜選択。自分の環境の場合はCOM3)
コードのVerify (Arduino: Verify)
Blink.ino 横のチェックマーク入りのボタン もしくは コマンドパレットからArduino: Verify
を選択する。
コードの焼きこみ (Arduino: Upload)
Blink.ino 横の下矢印ボタン もしくは コマンドパレットからArduino: Upload
を選択する。
COMポート設定不備などの場合は焼きこめずにエラーになる。
シリアルの読み出し (Arduino: Open Serial Monitor)
コマンドパレットから Arduino: Open Serial Monitor
を選択する。
ボーレート設定は画面下部の青いバーから行える。
なお、デフォルトのボーレートは拡張設定から設定できる。
余談: 購入したArduinoセット
Amazonで下記を購入した。
3000円程度でArduino Uno互換ボードにLCDやら抵抗やら諸々がついているのがよかった。 バッテリ無しの構成を探していたのでこれも決め手だった。
golang メモ
golang についてのメモ。適宜追加。
目次
組み込み関数
ドキュメント
下記にまとまっている。
builtin - The Go Programming Language
string
アクセス方法ごとの返り値
...
で展開した際はbyteが返るので、下記のようにしてbyte sliceにappendできる。
a := []byte{} b := "hoge" a = append(a, b...)
stringは配列のようにアクセスするとbyteが返る。またstringのサイズをlenで求めた場合、byte数が返る。
一方で、for rangeで回すとruneが返る。
よって、下記のようにループの回し方でそれぞれ異なる値が返る。
package main import "fmt" func main() { test := "hoge" for i := 0; i < len(test); i++ { fmt.Printf("type=%T val=%x\n", test[i], test[i]) } for _, v := range test { fmt.Printf("type=%T val=%c\n", v, v) } }
type=uint8 val=68 type=uint8 val=6f type=uint8 val=67 type=uint8 val=65 type=int32 val=h type=int32 val=o type=int32 val=g type=int32 val=e
slice
lengthとcapacity
sliceはlengthとcapacity を持つ。それぞれlen()
および cap()
というビルトイン関数で取得できる。いずれも戻り値はint型。
makeする際にcapcityを省略すると、capacityの値はlengthと等しくなる。
a := make([]int, 8) fmt.Printf("len=%d cap=%d\n", len(a), cap(a)) // len=8 cap=8
sliceは参照型
参照型なので、代入してから変更すると、元のsliceも更新される。
func printSlicePointer() { a := []byte{0xaa} b := a fmt.Printf("a=%p b=%p\n a=%x\n b=%x\n", a, b, a, b) b[0] = 0x12 fmt.Printf("a=%p b=%p\n a=%x\n b=%x\n", a, b, a, b) }
これの結果は下記。アドレスならびにbへの修正がaに対しても行われたかのように見える。
a=0xc00001a110 b=0xc00001a110 a=aa b=aa a=0xc00001a110 b=0xc00001a110 a=12 b=12
これを避けるにはcopyを使用して、同一内容のsliceを別途作成する。
sliceのcopy
// copy元のslice が var src []byteとする dst := make([]byte, len(src) ) copy(dst, src)
dstに十分なlengthがない場合、copyでpanicする。
sliceの一部を参照する
dst := src[start:end]
これによって、src[start] から src[end-1]の要素を参照する slice dst が生成される。len(dst) は end - startとなる。
参照なので、dstを修正した場合は、src側も修正される。
startとendは省略も可能。startを省略した場合は先頭から、endを省略した場合は最後尾まで、という意味になる。
array
宣言
a := [...]int{0, 1, 2, 3}
arrayを参照するsliceの生成
slice := array[:]
下記のようなコードを実行すると、sliceとarrayで同じアドレスを指しており、sliceはarrayの参照であることがわかる。
a := [4]int{0, 1, 2, 3} b := a[:] fmt.Printf("a=%p b=%p\n a=%x\n b=%x\n", &a, b, a, b)
この結果は下記。
a=0xc000014460 b=0xc000014460 a=[0 1 2 3] b=[0 1 2 3]
interface
cast
vInt, ok := i.(int)
switch
switch v := i.(type) { case int: fmt.Printf("int\n") case string: fmt.Printf("string\n") default: fmt.Printf("unknown\n") }
構造体のTag
encoding/json でよく見る。`key:"val"`
type sample struct { i int `json:"int"` f float32 `sample:"float"` }
バッククォート内ではスペースをつけて複数のタグを付与することもできる。
type sample struct { i int `json:"int" sample:"aaa"`
The Go Programming Language Specification - The Go Programming Language
タグの取得
reflectパッケージを使用する。reflect.StructFieldを取得し、そのメンバのTagを参照する。
- TypeOfでTypeを取得
- Kindで構造体か判別し、NumFieldで構造体個数を確認する。
- Fieldで構造体メンバを取得し、TagメンバのStructTagに対して、Get/Lookupする。
func readStructTag() { type S struct { V int `sample:"V"` W string `sample:"W"` I uint B float32 `sample:""` } s := S{} t := reflect.TypeOf(s) for i := 0; i < t.NumField(); i++ { f := t.Field(i) v, ok := f.Tag.Lookup("sample") if ok { fmt.Printf("%d:value=%s\n", i, v) } } }
0:value=V 1:value=W 3:value=
reflect - The Go Programming Language
cgo
公式Wiki
コメント
コンパイラディレクティブ
compile - The Go Programming Language
ビルド
https://golang.org/cmd/go/#hdr-Build_constraints
テスト
$ go test
サブディレクトリ以下もテストをする場合は、./...
を付与する。
$ go test ./...
コメントによる標準出力のテスト
// Output:
というコメントを付与し、改行後想定される出力をコメントで記述する。
//
の後にはスペースを挟む。
// Output: // 0x0f
出力内容が順不同の場合は、// Unordered output:
とする。
testing - The Go Programming Language
ベンチマーク
BenchmarkXxx(*testing.B) という関数を定義する。 Xxxとあるように、大文字である必要がある。
testing - The Go Programming Language
go test
にオプションを付与することでベンチマークが測定される。
go - The Go Programming Language
メモリ使用量やアロケート回数を調べたい場合は下記。
go test -bench . --benchmem
プロファイルを取得する
[Go] pprofでのプロファイル(計測)のやり方を改めて整理した - Qiita
go test
時に下記オプションを付与することで、go tool pprof
用のファイルを生成できる。
go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
ビルド
Cから使える共有ライブラリをビルドする
-buildmode=c-shared
を付与する。
go - The Go Programming Language
最適化状況を確認する
-gcflags "-m"
をつける。
Goのコンパイル時の最適化結果を確認する(インライン化の条件についても記載) - Qiita
最適化されるための条件。
CompilerOptimizations · golang/go Wiki · GitHub
コンパイルオプション一覧
compile - The Go Programming Language
golang 自体のビルド
リポジトリをチェックアウトして、バージョンbranchにスイッチ。
$ git clone https://go.googlesource.com/go goroot $ cd goroot $ git checkout go1.XX.X $ cd src $ ./all.bash
参考資料
- 最適化/高速化手法が複数公開されている。
Golangでバイナリ等のbit操作を行うためのライブラリ go-bit
加筆修正したものをこちらに書きました。v2.2.0からbinary.Write相当のAPI、bit.Writeをサポートしました。
Golangでバイナリ等のbit操作を行うためのライブラリ go-bit | Zenn
概要
go で 主にバイナリファイルの読み出し等に使えるbit操作ライブラリを作りました。*1
binary.Readのように、構造体にbit配列を定義して読みだすことができます。
目次
特徴
下記のようにbitを定義でき、bit.Readを使用することでいい感じに埋めて返してくれます。
package main import ( "bytes" "encoding/binary" "fmt" "github.com/nokute78/go-bit/pkg/bit/v2" "io" ) type BitMap struct { Bit0 bit.Bit Bit1 bit.Bit Bit2 bit.Bit Bit3 bit.Bit Reserved [4]bit.Bit `bit:"skip"` } func ReadBitMap(r io.Reader, b binary.ByteOrder) (*BitMap, error) { ret := &BitMap{} if err := bit.Read(r, b, ret); err != nil { return nil, err } return ret, nil } func main() { buf := bytes.NewBuffer([]byte{0xf5}) bm, err := ReadBitMap(buf, binary.LittleEndian) fmt.Printf("bm=%+v err=%s\n", bm, err) }
作った背景
レジスタやバイナリファイルの仕様を見ていると、bitごとに意味が割り当てられていて、これらの意味を解釈するためには、byte配列等で読みだした上で&や|の演算子を駆使し、bit演算をする必要があります。
また、golangにはmath/bits というbit操作用のパッケージがあるのですが、これは主に演算用のもので、上記のようなbitの取り出しには不向きなようでした。(四則演算やその際にキャリーするとか、ビットの並べ替えをするとか、そういう用途のようです。)
また、goにはencoding/binaryという便利なパッケージがあり、定義済の構造体をbinary.Readに投げれば、バイナリファイルをいい感じに解釈してその構造体に埋めて返してくれる機能があります。
まあ、binary.Readのbit版が欲しかったのです。構造体にbitの定義をして、投げればいい感じに埋めて返してくれるAPIが。
インストール
下記のようにv2をgo get してください。(トップディレクトリのものは古いです)
go get github.com/nokute78/go-bit/pkg/bit/v2
StuctTag
いくつかのStruct Tagをサポートしています。
タグ | 内容 |
---|---|
`bit:"skip"` |
このフィールドは無視します。オフセットはフィールドサイズ分だけ移動します。Reservedな値に対して使うと良いです。 |
`bit:"-"` |
このフィールドは無視します。オフセットは移動しません。 |
`bit:"BE"` |
このフィールドはBigEndianとして扱う。Mixed Endianなデータの解析に便利。(ただしbit向きでなく、後述のbyte array用) |
`bit:"LE"` |
このフィールドはLittleEndianとして扱う。Mixed Endianなデータの解析に便利。(ただしbit向きでなく、後述のbyte array用) |
サンプルコード
zipファイルにはヘッダにgeneral purpose bit flagという16bitのデータ構造を持っているので、それを読んでみましょう。 *2
ファイルヘッダについては4.3.7 、general purpose bit flagについては、下記の4.4.4を参照しました。 https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
下記はサンプルコードです。
zip file header を構造体として定義し、それをbit.Readに渡しているのがポイントです。
package main import ( "encoding/binary" "flag" "fmt" "github.com/nokute78/go-bit/pkg/bit/v2" "os" ) type ZipHeader struct { Signature uint32 MinVersoin uint16 // general purpose bit flag: 2bytes Encrypted bit.Bit CompMode [2]bit.Bit Crc32CompUnCompUsed bit.Bit ReservedForMethod8 bit.Bit `bit:"skip"` CompPatchedData bit.Bit StrongEncryption bit.Bit Unused [4]bit.Bit `bit:"skip"` LanguageEncoding bit.Bit Reserved bit.Bit HideLocalHeader bit.Bit Reserved2 [2]bit.Bit `bit:"skip"` CompMethod uint16 LastModTime uint16 LastModData uint16 Crc32 uint32 CompSize uint32 UnCompSize uint32 FileNameLen uint16 ExtraFieldLen uint16 } func main() { flag.Parse() var zh ZipHeader for _, v := range flag.Args() { f, err := os.Open(v) if err != nil { fmt.Fprintf(os.Stderr, "Open(%s): err=%s\n", v, err) continue } err = bit.Read(f, binary.LittleEndian, &zh) if err != nil { fmt.Fprintf(os.Stderr, "Read(%s): err=%s\n", v, err) f.Close() continue } fmt.Printf("header(%s):%+v\n", v, zh) f.Close() } }
go のインストールディレクトリにはテスト用のzipファイルがいくつか有ったので、試しに読んでみました。
$ go run zipexample.go /usr/local/go/src/archive/zip/testdata/dd.zip header(/usr/local/go/src/archive/zip/testdata/dd.zip):{Signature:67324752 MinVersoin:20 Encrypted:0 CompMode:[0 0] Crc32CompUnCompUsed:1 ReservedForMethod8:0 CompPatchedData:0 StrongEncryption:0 Unused:[0 0 0 0] LanguageEncoding:0 Reserved:0 HideLocalHeader:0 Reserved2:[0 0] CompMethod:8 LastModTime:26826 LastModData:15938 Crc32:0 CompSize:0 UnCompSize:0 FileNameLen:8 ExtraFieldLen:0}
Bit3(Crc32CompUnCompUsed)が立っていますね。これが立っているとcrc-32/compressed size/uncompressed size が0になるそうです。それらがキチンと0になっている様子が見られます。
下記のようにBit3が落ちている場合の例も貼っておきます。これらはcrc-32や各サイズが非0ですね。
$ go run zipexample.go /usr/local/go/src/archive/zip/testdata/unix.zip header(/usr/local/go/src/archive/zip/testdata/unix.zip):{Signature:67324752 MinVersoin:10 Encrypted:0 CompMode:[0 0] Crc32CompUnCompUsed:0 ReservedForMethod8:0 CompPatchedData:0 StrongEncryption:0 Unused:[0 0 0 0] LanguageEncoding:0 Reserved:0 HideLocalHeader:0 Reserved2:[0 0] CompMethod:0 LastModTime:20620 LastModData:16264 Crc32:2098461837 CompSize:8 UnCompSize:8 FileNameLen:5 ExtraFieldLen:28}
なお、README.mdではBig Endianでも動作するか確認するため、一例としてTCPHeaderのパースを行っています。
そのほか
golangのbinary.Readでは、例えば6byteをBig Endianで読むということができないようです。
https://play.golang.org/p/RUvRdkc0a0W
https://github.com/golang/go/issues/40891
数値型のbyteサイズでエンディアンが判定される様子。
bit.Readではbyte arrayを渡せばエンディアンを反映して埋めてくれるようにしてあり、そこはbinary.Readと非互換です。
Go1.15 からのreflectパッケージの挙動の違いについて
追記
Go1.15 からのreflectパッケージの挙動の違いについて | Zenn にも掲載してみました。
概要
Go1.15からreflectパッケージの一部挙動が変わった。これにより、Go1.14以前で動作していたコードがPanicしうる。
非公開構造体を埋め込み、その構造体のメソッドをCallした場合にPanicする。
目次
リリースノートの記述
Go 1.15のリリースノートによると、reflectパッケージについて下記記述がありました。
Package reflect now disallows accessing methods of all non-exported fields, whereas previously it allowed accessing those of non-exported, embedded fields. Code that relies on the previous behavior should be updated to instead access the corresponding promoted method of the enclosing variable.
サンプルコード
サンプルコードは下記です。二つの構造体を定義しています。
- 非公開の
unexportedStruct
Printするだけのメソッドを持つ。 - 公開の
R
上記unexportedStructが埋め込まれている。
Rを用いて、unexportedStruct内のメソッドにアクセスしようとするコードです。エラー処理は省いています。
package main import ( "fmt" "reflect" ) type unexportedStruct struct{} func (s unexportedStruct) Print() { fmt.Println("Print!") } type R struct { unexportedStruct } func main() { v := reflect.ValueOf(R{}) fmt.Printf("NumMethod=%d\n", v.Field(0).NumMethod()) method := v.Field(0).Method(0) fmt.Printf("Method=%v\n", method) method.Call(nil) }
Go1.14とGo1.15の違い
下記が挙動の違いです。Go 1.14.7 ではPrint関数が実行されるのに対し、Go1.15ではPanicして落ちます。
Go 1.15
$ go version go version go1.15 linux/amd64 $ go run unexported.go NumMethod=1 Method=0x48f340 panic: reflect: reflect.Value.Call using value obtained using unexported field goroutine 1 [running]: reflect.flag.mustBeExportedSlow(0x2b3) /usr/local/go/src/reflect/value.go:237 +0x131 reflect.flag.mustBeExported(...) /usr/local/go/src/reflect/value.go:228 reflect.Value.Call(0x4dd980, 0x5daf48, 0x2b3, 0x0, 0x0, 0x0, 0x1, 0x10, 0x0) /usr/local/go/src/reflect/value.go:335 +0x52 main.main() /home/taka/tmp/unexported.go:22 +0x1ef exit status 2
Go 1.14
$ go version go version go1.14.7 linux/amd64 $ go run unexported.go NumMethod=1 Method=0x488b20 Print!
修正コミット
この辺ですかね。
所感
Go1.14までの挙動がバグっぽいので適切に修正されたのかなと。
けれどもユーザーとしてはPanicするか否かをどう判別するのがいいんでしょうかね。いずれもNumMethod()の返り値は同じですし、CanCallのようなチェック関数もないですし。
fluent-bit の lua filter plugin 使用例
概要
fluent-bit には Lua スクリプトでfilterできるプラグインがあります。 これを使うと、下記のようなことができます。
- recordに情報を追加する
- 特定の条件にマッチしたrecordをフィルタする
- record内の特定の情報を削除する
目次
使用方法
fluent-bitにはLuaスクリプトのパスと、スクリプトに記載されている関数名を伝える必要があります。 たとえば、下記の例は、test.luaスクリプトをロードし、そこに含まれるtest_func関数を実行するという意味になります。
[INPUT] Name dummy Tag test Dummy {"foo":"bar"} [FILTER] Name lua Match * script ./test.lua call test_func [OUTPUT] Name stdout Match *
制約
Fluent-bitのLuaは数値型としてdoubleを使用しています。 そのため、下記のような問題が発生する可能性があります。
前者については、Type_int_key
というプロパティを指定することで、フィルタ後の値を再度整数型に変更してくれるようになります。
Lua - Fluent Bit: Official Manual
後者については、良い解が思いついていません。パッチをお待ちしております。
Lua filters can mangle timestamps · Issue #2015 · fluent/fluent-bit · GitHub
luaスクリプト側の仕様
下記のように引数と戻り値がそれぞれ3つずつ必要です。
function test_func(tag, timestamp, record) -- something to do return ret, timestamp, new_record end
fluentdのeventを構成するtag, timestampとLuaの連想配列形式のrecordが与えられ、戻り値としてfilter結果を示すretとtimestamp、そしてrecordを返却します。 ここで、retはfilter結果に応じて、下記のいずれかを返す必要があります。
filter結果 | retの値 | 備考 |
---|---|---|
削除した | -1 | 不要なeventをフィルタリングする用途 |
何も変更しなかった | 0 | |
recordとtimestampを変更した | 1 | |
recordのみ変更した | 2 | v1.4.3より対応。timestampの丸め誤差を防止するための機能。 |
使用例
それでは使用例を紹介していきます。
時刻情報をrecordに加える
フィルタした時刻を保持したい場合には下記のように行います。
configuration
test.lua:
function test_func(tag, timestamp, record) new_record = record new_record["filterd_time"] = os.time() new_record["filterd_date"] = os.date() return 2, timestamp, new_record end
input
{"foo"=>"bar"}
output
{"filterd_date"=>"Sun May 31 09:10:59 2020", "foo"=>"bar", "filterd_time"=>1590883859.000000}
タグをrecordに加える
tagというkey名でtagの文字列を追加するスクリプトです。
configuration
test.lua:
function test_func(tag, timestamp, record) new_record = record new_record["tag"] = tag return 2, timestamp, new_record end
test.conf:
[INPUT] Name dummy Tag test Dummy {"foo":"bar"} [FILTER] Name lua Match * script ./test.lua call test_func [OUTPUT] Name stdout Match *
input
{"foo"=>"bar"}
output
{"foo"=>"bar", "tag"=>"test"}
"tag"=>"test"
が付与されています。
特定のkey/value ペアを削除する
record内の一部ペアを消去する例です。
configuration
test.conf:
[INPUT] Name dummy Tag test Dummy {"important":"value", "drop":"value"} [FILTER] Name lua Match * script ./test.lua call test_func [OUTPUT] Name stdout Match *
test.lua:
function test_func(tag, timestamp, record) if record["drop"] == nil then return 0, timestamp, record end new_record = record new_record["drop"] = nil return 2, timestamp, new_record end
input
{"important"=>"value", "drop"=>"value"}
output
{"important"=>"value"}
"drop":"value"
というペアが消えています。
特定のkeyを含むrecordを捨ててフィルタする
例えば不要なデータ通信を行わないように、record内に特定のkeyが含まれていた場合にそのrecord全体を捨てるユースケースがあるかと思います。
下記は、"drop" というkeyが含まれている場合にrecord全体を捨てる例になります。
configuration
[INPUT] Name dummy Tag test Dummy {"important":"value"} [INPUT] Name dummy Tag test Dummy {"drop":true} [FILTER] Name lua Match * script ./test.lua call test_func [OUTPUT] Name stdout Match *
test.lua:
function test_func(tag, timestamp, record) key = "drop" if record[key] ~= nil then return -1, timestamp, record end return 0, timestamp, record end
input
{"important"=>"value"} {"drop"=>true}
output
{"important"=>"value"}
{"drop":true}
というrecordが出力されなくなるかと思います。
特定のkey名を変更する
configuration
test.conf
[INPUT] Name dummy Tag test Dummy {"foo":"bar"} [FILTER] Name lua Match * script ./test.lua call test_func [OUTPUT] Name stdout Match *
test.lua:
function test_func(tag, timestamp, record) new_record = record if record["foo"] == nil then return 0, timestamp, new_record end new_record["hoge"] = record["foo"] new_record["foo"] = nil return 2, timestamp, new_record end
input
{"foo"=>"bar"}
output
{"hoge"=>"bar"}
"foo"というkeyが"hoge"に変わっています。
UEFI Shell の コマンド
概要
UEFI Shellについてのメモ。
目次
- 概要
- 目次
- UEFI Shellとは
- UEFI Shell の動作環境構築
- リンク
- ユースケース
- シェルスクリプト
UEFI Shellとは
UEFI Firmware(BIOSに相当)上で動作するShellのこと。 簡単なファイル操作をはじめ、NW通信なども行うことができる。
UEFI Shell の動作環境構築
Ubuntu 20.04上のQEMU上でShellを試す
インストール方法
sudo apt install qemu-kvm
起動方法
qemu-system-x86_64 -bios OVMF.fd
Shell上からホスト上のディレクトリにアクセスできるようにする
-drive
オプションで指定する。
qemu起動後、ホストからディレクトリの内容を書き換えても、UEFI Shell上からは認識できないので注意すること。
qemu-system-x86_64 -bios OVMF.fd -drive file=fat:rw:path/to/dir
リンク
ソース
この辺。 github.com
ユースケース
help
コマンドの一覧 や 各コマンドの内容について調べる
helpコマンドを使う。
引数無しの場合はサポートしているコマンド一覧を確認できる。
help
コマンド名を引数に与えることでそのコマンドのhelpを参照できる。
help ls
ファイル/ディレクトリ
ファイルパスについて
Windowsライク。 パスの区切りはバックスラッシュ'\'。 また、先頭にボリュームラベル名を付与することで、そのマップ以下のディレクトリやファイルを指定できる。(Windowsのドライブレターのよう)
下記はボリュームラベルfs0
以下にマップされているhogeディレクトリを指定する絶対パス。
fs0:\hoge
ワイルドカードもサポートされている。
マウント可能な一覧を確認する(map)
> map Mapping table FS0: Alias(s):HD0a1:;BLK1: PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1) BLK0: Alias(s): PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0) BLK2: Alias(s): PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
ファイルシステムをマウントする
下記のようにShell>
という表示の場合はファイルシステムがマウントされていない。
Shell>
マウントしたい対象にコロンを加えた文字列を入力することでマウントできる。
下記はfs0
をマウントする場合の例。
fs0:
マウントされた場合は、Shell>
という表記が変わり、ボリュームラベル名:ディレクトリパス
という表記になる。
fs0:\>
ディレクトリの一覧を表示する(ls/dir)
>ls Directory of: FS0:\ 05/29/2020 22:48 11,077 NvVars 05/29/2020 22:51 2 ls.txt 2 File(s) 11,079 bytes 0 Dir(s)
dir
コマンドもあるが、ls
へのエイリアスとなっている。
ファイルのコピー(cp/copy)/消去(rm/del)/移動・リネーム(mv/move)
UNIXコマンドのように使用可能。
コピー操作。
cp src dst
リネーム操作。
mv aaa bbb
消去操作。
また、rm
のエイリアスとして、del
が設定されている。
rm src
ディレクトリの作成(mkdir/md)/消去(rm)
mkdir hoge
mkdir
のエイリアスとしてmd
が用意されている。
ディレクトリの消去はrmdir
でなく、rm
コマンドを使用する。
rmdir hoge
ファイル内容を出力する(type/cat)
ファイル内容を標準出力に表示することができる。
type filepath
type
のエイリアスとしてcat
が設定されている。
タイムスタンプの更新(touch)
パス先のファイルのタイムスタンプを更新する。
なお、このコマンドで空ファイルの作成は行えないようだ。
touch filepath
ネットワーク
ネットワーク設定(ifconfig)
ifconfig コマンドを使う。
-l
オプションでinterface名を取得して、-s
コマンドと組み合わせてIPを割り当てる。
下記はeth0について、DHCPで割り当てる例。
ifconfig -s eth0 dhcp
設定の確認は-l
オプション。
ifconfig -l
疎通確認(ping)
ping
ユーティリティ
テキストエディタ(edit)
フルスクリーンのエディタが立ち上がる。 ショートカットは下記の様子。
Ctrl+Tを押下することで、ASCIIかUNICODEかを切り替えることができる。
機能 | ショートカット |
---|---|
終了 | Ctrl+Q |
ファイル保存 | Ctrl+S |
ファイルオープン | Ctrl+O |
検索 | Ctrl+F |
置換 | Ctrl+R |
カット | Ctrl+K |
ペースト | Ctrl+U |
行移動 | Ctrl+G |
文字コード切り替え | Ctrl+T |
ヘルプ | Ctrl+E |
バイナリエディタ(hexedit)
カット/ペーストがテキストエディタと異なるので注意。
ファイルのほか、-d
でディスクを、-m
でメモリもオープンできるらしい。
機能 | ショートカット |
---|---|
終了 | Ctrl+Q |
バッファ保存 | Ctrl+S |
カット | Ctrl+X |
ペースト | Ctrl+V |
選択開始 | Ctrl+T |
選択終了 | Ctrl+D |
ファイルオープン | Ctrl+O |
ディスクオープン | Ctrl+I |
メモリオープン | Ctrl+M |
オフセット移動 | Ctrl+G |
ヘルプ | Ctrl+E |
システム情報
エイリアス設定(alias)
オプション無しで、現在のエイリアスを確認できる。
>alias cat:type cd..:cd .. cd\:cd \ copy:cp del:rm dir:ls md:mkdir mem:dmem mount:map move:mv ren:mv
UEFI Shellや UEFIのバージョンを確認する(ver)
UEFI Shellのバージョンと、UEFI ShellがアクセスするUEFI 自体のバージョンを確認できる。
> ver UEFI Interactive Shell v2.2 EDK II UEFI v2.70 (EDK II, 0x00010000)
UEFI変数を操作する(dmpstore)
引数無しの場合、すべてのUEFI変数とその属性ならびに内容がダンプされる。
dmpstore
消去を行う場合は、-d
オプション。
タイムゾーンの表示/変更(timezone)
オプションが無い場合、現在のタイムゾーンが表示される。
>timezone LOCAL
タイムゾーンの一覧は-l
オプションで確認できる。
timezone -l
タイムゾーンの設定は、-s
オプションとUTCからのオフセットで設定できる。
timezone -s -9:00
時刻の確認/変更(time)
オプションが無い場合、現在時刻とUTCのオフセットが表示される。
>time 00:34:52 (UTC-09:00)
時刻設定は引数にhh:mm:ss
のフォーマットで指定できる。
time 12:00:59
日付の確認/変更(date)
オプションが無い場合、現在の日付が表示される。フォーマットは、MM/DD/YYYY
> date 05/29/2020
引数で日付情報を与えることで変更できる。
date 06/30/2020
文字列や環境変数の表示(echo)
また、スクリプトファイル実行時の出力制御の設定変更を行うことができる。
> echo hoge hoge
環境変数表示の例は下記。変数名を%
でくくる。
> echo %lasterror% 0xE
引数無しの場合は、スクリプト実行時の出力設定を確認できる。
この設定は、-on
もしくは -off
オプションで変更できる。
> echo Echo is on.
環境変数の確認/設定(set)
オプション無しの場合、現在の環境変数一覧を確認できる。
> set
変数名と内容を下記のように指定できる。
set key val
ディスク容量を確認する(vol)
引数無しで、現在のボリュームラベル上の使用量や空き容量を確認できる。
> vol
ボリュームラベル名を付与することで、そのボリューム以下の情報を確認できる。
> vol fs0 Volume QEMU VVFAT (rw) 528171008 bytes total disk space 528056320 bytes available on disk 8192 bytes in each allocation unit
ハードウェア
PCIデバイスの一覧表示(pci)
何もオプションがない場合、簡素な一覧が表示される。
> pci Seg Bus Dev Func --- --- --- ---- 00 00 00 00 ==> Bridge Device - Host/PCI bridge Vendor 8086 Device 1237 Prog Interface 0 00 00 01 00 ==> Bridge Device - PCI/ISA bridge Vendor 8086 Device 7000 Prog Interface 0 00 00 01 01 ==> Mass Storage Controller - IDE controller Vendor 8086 Device 7010 Prog Interface 80 00 00 01 03 ==> Bridge Device - Other bridge type Vendor 8086 Device 7113 Prog Interface 0 00 00 02 00 ==> Display Controller - VGA/8514 controller Vendor 1234 Device 1111 Prog Interface 0 00 00 03 00 ==> Network Controller - Ethernet controller Vendor 8086 Device 100E Prog Interface 0
バス番号、デバイス番号、ファンクション番号を付与することで、対象のコンフィグ空間の情報を確認できる。
さらに-i
オプションを付与することで人が読みやすい情報を付与して表示してくれる。
> pci 00 00 00 -i PCI Segment 00 Bus 00 Device 00 Func 00 [EFI 0000000000] 00000000: 86 80 37 12 07 00 00 00-02 00 00 06 00 00 00 00 *..7.............* 00000010: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 00000020: 00 00 00 00 00 00 00 00-00 00 00 00 F4 1A 00 11 *................* 00000030: 00 00 00 00 00 00 00 00-00 00 00 00 FF 00 00 00 *................* 00000040: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 00000050: 00 00 00 00 00 00 00 10-00 00 01 00 00 00 00 00 *................* 00000060: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 00000070: 00 00 02 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 00000080: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 00000090: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 000000A0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 000000B0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 000000C0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 000000D0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 000000E0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* 000000F0: 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 *................* Vendor ID(0): 8086 Device ID(2): 1237 Command(4): 0007 (00)I/O space access enabled: 1 (01)Memory space access enabled: 1 (02)Behave as bus master: 1 (03)Monitor special cycle enabled: 0 (04)Mem Write & Invalidate enabled: 0 (05)Palette snooping is enabled: 0 (06)Assert PERR# when parity error: 0 (07)Do address/data stepping: 0 (08)SERR# driver enabled: 0 (09)Fast back-to-back transact...: 0 Status(6): 0000 (04)New Capabilities linked list: 0 (05)66MHz Capable: 0 (07)Fast Back-to-Back Capable: 0 (08)Master Data Parity Error: 0 (09)DEVSEL timing: Fast (11)Signaled Target Abort: 0 (12)Received Target Abort: 0 (13)Received Master Abort: 0 (14)Signaled System Error: 0 (15)Detected Parity Error: 0 Revision ID(8): 02 BIST(0F): Incapable Cache Line Size(C): 00 Latency Timer(D): 00 Header Type(0E): 00, Single function, PCI device Class: Bridge Device - Host/PCI bridge -
終了/シャットダウン/再起動
UEFI Shell を終了する(exit)
exit
シャットダウン/再起動する(reset)
引数無しの場合、再起動処理が実行される。
reset
シャットダウンを行う場合は、-s
オプションを付与する。
reset -s
汎用的な情報
コマンドの出力をページ毎にいったん止める
おおよそのコマンドは -b
オプションを付与することで、
一ページ単位で表示を止めてくれるようになる。
表示が止まったのち、Enterで改ページ、qでキャンセルを行うことができる。
エスケープシーケンス
下記はスペースを含むディレクトリ生成の例。
mkdir aaa^ bbb
ファイルへのリダイレクト
>
によってファイルにリダイレクトできる。
dmpstore > dmpstore.txt
シェルスクリプト
シェルスクリプトの拡張子は.nshである。 UEFI Shell起動後、一定時間が経過した際に、startup.nsh というファイルが存在する場合は、そのファイルが自動実行される。
MessagePackを解析するためのライブラリ/ツール msgpack-microscope
概要
Golangで、MessagePackの解析用ライブラリとそれを使用したサンプルツールを作成しました。
ビルド済みバイナリはReleases から取得できます。
MessagePackとは
JSONのようなデータ構造を、バイナリ形式で表現したデータ形式です。 大抵の場合、JSONよりもデータサイズを減らすことができるようですが、バイナリ列のために可読性が下がっています。 公式サイトの例ですと、
{"compact":true,"schema":0}
という27byteのJSONを、
82 a7 63 6f 6d 70 61 63 74 c3 a6 73 63 68 65 6d 61 00
という18byteのバイナリに変換する例が示されています。
特徴
基本的には、インスパイア元のmsgpack-inspectとほぼ同じですが、下記が異なっています。 *1
- HTTP POST経由のデータを解析することができる
- Timestamp extension type をサポート
- Fluentd の EventTime Ext Format をサポート*2
- 破損データ*3に少し耐性がある
サンプルツール msgpack2json
その名のとおり、MessagePackをJSONに変換するためのサンプルツール。 出力フォーマットは、同じ機能のツールであるmsgpack-inspectの形式をほぼ流用しています。
下記のいずれかからMessagePackのバイト列を受け取り、標準出力に解析結果を出力します。
- 標準入力(他コマンドからパイプで繋ぐケース)
- ファイル
- HTTP POST
それぞれの動作サンプルについて以下に書きます。なお、いずれのケースについても下記JSON相当のMessagePackを解析した結果になります。
{"compact":true,"schema":0}
標準入力から読む
パイプでリダイレクトするケースを想定した使い方です。
$ printf "\x82\xa7compact\xc3\xa6schema\x00" | ./msgpack2json
出力結果は下記。
{"format":"fixmap", "header":"0x82", "length":2, "raw":"0x82a7636f6d70616374c3a6736368656d6100", "value": [ {"key": {"format":"fixstr", "header":"0xa7", "raw":"0xa7636f6d70616374", "value":"compact"}, "value": {"format":"true", "header":"0xc3", "raw":"0xc3", "value":true} }, {"key": {"format":"fixstr", "header":"0xa6", "raw":"0xa6736368656d61", "value":"schema"}, "value": {"format":"positive fixint", "header":"0x00", "raw":"0x00", "value":0} } ] }
ファイルを読む
バイナリファイルを読むケースです。
$ printf "\x82\xa7compact\xc3\xa6schema\x00" > b.msgp $ ./msgpack2json b.msgp
出力結果は標準入力と同様なので割愛。
HTTP POST
簡単なhttp server機能も備えています。-sオプションでserver動作をし、デフォルトで8080ポートへのPOSTを待ちます。 なお、ポート番号については、-p オプションで変更することができます。
$ printf "\x82\xa7compact\xc3\xa6schema\x00" > b.msgp $ ./msgpack2json -s &
たとえば、curlで下記のように投げることで出力結果を得ることができます。
$ curl -sS localhost:8080 -X POST --data-binary "@b.msgp"
Fluent-bitのout_httpプラグインなど、HTTP POST形式でMessagePackを投げるソフトウェアのデバッグなどにも使うことができます。
その他オプションについて
- -e : Fluentd の EventTime Ext Format を解釈可能とする。
Fluentdでは、Ext Typeを使用して、独自のタイムスタンプ形式を定義しています。このオプションを使うことで、そのタイムスタンプ形式を解釈できるようになります。
$ printf "\xd7\x00\x5c\xda\x05\x00\x00\x00\x00\x00"| ./msgpack2json -e
{"format":"event time", "header":"0xd7", "type":0, "raw":"0xd7005cda050000000000", "value":"2019-05-14 09:00:00 +0900 JST"}
- -r: プレーンなJSON形式で出力する
解析用でない、シンプルな形式で出力します。
$ printf "\x82\xa7compact\xc3\xa6schema\x00"|./msgpack2json -r
{"compact":true,"schema":0}
ライブラリ
基本的には、Decode関数にMessagePackの[]byteを与えることで、MPObjectに変換し、そのMPObjectの中身をゴニョゴニョして解析する形になります。
type MPObject struct { FirstByte byte /* どのフォーマットかを示すbyte */ FormatName string /* フォーマット名称 */ ExtType int8 /* Ext フォーマットの場合、どのTypeかを示す。それ以外のフォーマットの場合は0。*/ Length uint32 /* length を備えるフォーマットの場合のみ。*/ DataStr string /* 各フォーマットのデータを文字列にしたもの */ Raw []byte /* FirstByteからデータ本体までのbyteスライス。ArrayやMapの場合は、その要素も含む。 */ Child []*MPObject /* ArrayやMap フォーマットの場合、その各要素が保存される。*/ }
import
import "github.com/nokute78/msgpack-microscope/pkg/msgpack"
サンプルコード
簡単なサンプルコードは下記です。
package main import ( "bytes" "fmt" "github.com/nokute78/msgpack-microscope/pkg/msgpack" "os" ) func showMsgPack(obj *msgpack.MPObject) { switch { case msgpack.IsMap(obj.FirstByte) || msgpack.IsArray(obj.FirstByte): for _, v := range obj.Child { showMsgPack(v) } default: fmt.Fprintf(os.Stdout, "%s\n", obj) } } func main() { /* {"compact":true,"schema":0} in JSON */ msgp := []byte{0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00} ret, err := msgpack.Decode(bytes.NewBuffer(msgp)) if err != nil { os.Exit(-1) } showMsgPack(ret) }
出力結果は、下記のようになります。
fixstr(0xa7): val="compact" true(0xc3): val=true fixstr(0xa6): val="schema" positive fixint(0x00): val=0
独自のExt Typeを定義する
MessagePackには、Ext Typeを用いることでユーザーが独自フォーマットを定義することができます。 例としては、FluentdのEventTimeがあります。
本ライブラリでは、下記のstruct埋め、RegisterExt関数に食わせることで、ユーザー定義のフォーマットを追加することができます。
type ExtFormat struct { FirstByte byte /* どのフォーマットかを示すbyte。0xd4-0xd8, 0xc7-0xc9のいずれか。 */ ExtType int8 /* 種別を示す。正の整数。負の値は予約されているため、通常は使わない。*/ TypeName string /* Type 名称 */ DecodeFunc func([]byte) string /* []]byte列を変換するための関数 */ }
FluentdのEventTimeの場合の例は下記のようになります。
func extEventTimeV1(b []byte) string { if len(b) != 8 { return "" } var sec int32 if binary.Read(bytes.NewReader(b[:4]), binary.BigEndian, &sec) != nil { return "" } var nsec int32 if binary.Read(bytes.NewReader(b[4:]), binary.BigEndian, &nsec) != nil { return "" } return fmt.Sprintf("%v", time.Unix(int64(sec), int64(nsec))) } // RegisterFluentdEventTime registers Fluentd ext timestamp format. // https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1 */ func RegisterFluentdEventTime() { msgpack.RegisterExt(&msgpack.ExtFormat{FirstByte: msgpack.FixExt8Format, ExtType: 0, TypeName: "event time", DecodeFunc: extEventTimeV1}) msgpack.RegisterExt(&msgpack.ExtFormat{FirstByte: msgpack.Ext8Format, ExtType: 0, TypeName: "event time", DecodeFunc: extEventTimeV1}) }