技術書典6の戦利品を読む①〜OS Girls / 組込みエンジニアの教科書 / Rust on Bare-metal Raspberry Pi / Atmelさんちの消失〜

はじめに

1つ1つは短めです。 少しでも、宣伝、フィードバック、になれば幸いです。

OS Girls

booth.pm

8086やIntel SDMの紙媒体が転がっている不思議な部室で繰り広げられる女子高生たちのOS自作物語です。

物語の導入として、PCを使っていて湧いた疑問から、低レイヤに入っていくのは良いなぁ、と思いました。 OSってなんだろう、でOSの概念を説明しているのも、手を動かす一辺倒にならないように工夫されているように感じました。

次の階層でお会いしましょう、とのことですが、ringを使い切っているので、次はVMXですかね。

組込みエンジニアの教科書

組込みエンジニアの教科書

組込みエンジニアの教科書

技術書典で先行販売していたので、入手しました。 座学的な内容と、手を動かす内容とが、両方入っています。

座学的な内容は、エンベデッドスペシャリストの参考書に似たような感じでした。

組込みLinuxの章があり、ここでは、著者の経験から組込みLinuxをやる上で気をつけないといけないことが書かれています。 現場で痛い目を見ないと中々実感できない内容であり、組込み初心者の方にも知っておいて欲しい情報が書かれていました。 炎上しないように良いモノ作ろうとすると、ハードもソフトも学ばねばなりません。 一気には無理なので、できるところから守備範囲を広げましょう。 良い組込み開発の上司を見分ける基準として、使うのも良いと思います。

Rust on Bare-metal Raspberry Pi

booth.pm

Rustに限らず、ベアメタルプログラミングをやろうとすると、ハードウェアの説明が必要不可欠です。 そこが面倒で中々本の執筆ができていないわけなのですが。

本書では、そこの前提知識が丁寧に書かれていました。 そのため、少し、Rust成分は控えめです。 次回作で、Rustがたくさん出てくると良いなぁ、と思いました。

Atmelさんちの消失

booth.pm

こういうマイコンおよび製造メーカが辿った変遷は、非常に興味深いです。 が、自分では、中々実際の調査までしないため、まとまったものがあるととてもありがたいです。

自分がまだプログラマやっていなかった時代の空気も感じることができて、大変おもしろかったです。

組込みRustのイベントやります 2019/6/17

はじめに

inteface-meet-up.connpass.com

CQ出版様の雑誌インタフェース主催で、組込みRustのイベントを開催することになりました。 CQ出版様、ありがとうございます。

参加枠埋まってますが、繰り上がりや、多くの方が参加登録して頂ければまた別の機会を設けることもできるかもしれないので、奮って参加登録下さい。

当日、どういう発表するか、も含めて、これから詰めていく状況です。 要望などあれば、twitterなどでご意見頂けると、反映される可能性がございます。

私個人の想い

現状、組込みRustに取り組んでいる人口は、多くないと思います。 組込みRustをやっている人が居てもも、点在している状況で、組織立って動けたり、情報交換できる状態ではない認識です。

今回のイベントを機に、実は社内で勉強会やってて、とか、個人ではやってるんだけど中々組織に働きかけるのが…、とか、興味はあるんだけど手が出せてなくて、という方々が繋がれると良いなぁ、と思っています。

できる限り、情報をオープンにして、少しでも組込みRustの普及に貢献したいですね。

組込みでCからRustにステップアップする順序

はじめに

先日、twitterで以下のアンケートを行ったところ、予想以上に多くの方にご回答頂けたため、まとめと私の考えを書きます。

日頃C言語を主に利用している開発者に、Rustをおすすめするアプローチを探っています。 CとRustは、言語仕様の乖離が激しい、という事実があります。そのため、アプローチの仕方を間違えると拒絶反応を引き起こしそうです。

そこで、私以外の人がどう考えているか知りたくなったため、アンケートを行ってみました。

私の場合

Rustを学び始めた時点で、私は次の状況でした。

  • C++11/14を業務で1年
  • Haskell入門を途中挫折 (すごいH本を半分くらいまで)

あまり違和感なく、Rustに入門できたと思います。 C++Haskellの次の知識が入門で役立ちました。

  • C++
    • ムーブ
    • スマートポインタ
    • テンプレート
  • Haskell

また、日頃のプログラミングを行う中で、可能な限りイミュータブルな作りにしたり、型指向の考え方を多少身につけていました。

C++の知識はRust習得に確実に役立ちますが、学習しにくい部分があると感じています。 それは、新旧の情報が混ざっている点と、○○できるけどしてはいけないというプラクティスが多すぎる点です。 (ちなみに私はCもC++もけっこう好きです)

そのため、うまく最初からRustを触ってもらう方法を考える方が良いような気がしています。

アンケート結果

投票率の高い順から見ていきましょう。

不要

CからRustに一気にジャンプしよう派です。

私も、今はこの派閥です。 C++11以降や近代的なプログラミング言語 (Scalaなど) を学ぶコストと、Rustを学ぶコストとが、それほど変わらないように感じているためです。

C++

Cとの互換性が高く、Rustとも比較的似ている言語です。 C++11以降の知識は、Rustを理解する上で役立ちます。

また、組込みエンジニアも、アプリケーションはC++で書くこともあるため、実務をこなしながらRustの素養を身につけることができる点は、大きいです。 この項目に回答が集まったことは、妥当な感じを受けます。

Haskell

17%も入ったのは意外でした。 Rustは関数型のパラダイムも多く取り入れているので、その部分の理解を意図した回答だと思われます。

その他

ScalaとF#を推す声がありました。 Rustでは、オブジェクト指向、関数型に加え、所有権もあるため、非常に複雑になっています。

そこで、ScalaやF#のような言語で、オブジェクト指向、関数型、および、両者を組み合わせるパラダイムを理解した上で、Rustに臨むのが良いのではないか、ということでした。

その他、ご意見、ご感想、お待ちしております。

技術書典6買い物メモ

はじめに

自分用メモです。

f:id:tomo-wait-for-it-yuki:20190414195515j:plain

1日の流れ

10:10分ごろ会場に到着しましたが、すでにかなりの行列でした。 入場券の番号は2111でした。

11:30頃、会場入りしたと思います。

ハードウェア、低レイヤ、セキュリティ、と島を巡りました。 12:15頃には買い物を終えました。

紙で欲しかったのに変えなかった本が1冊ありましたが、概ね満足です。

買い物

ここ数ヶ月で、ハードウェアとセキュリティへの関心が高まったため、低レイヤ+ハードウェア+セキュリティ、と欲しいものが盛り沢山でした。

ハードウェア関連

  • 自分でつくるゲームボーイのカートリッジ
  • Atmelさんちの消失
  • つくろう!モータードライバ
  • 基板を作ってみよう〜KiCad 5による基板作成〜
  • USB雑記帳

低レイヤ

  • Linuxのプロセススケジューラ
  • ACPI規格書とっかかり教室
  • Local APICタイマー入門
  • UEFI読本〜GRUB編〜
  • Process Scheduler Blossom
  • フルスクラッチで作る!x86_64自作OS パート4
  • ゼロから作るRAW現像
  • OS Girls

セキュリティ

  • Radare2で学ぶバイナリ解析入門〜CTF対応〜
  • 動的バイナリ解析の基礎 with Intel Pin
  • たのしいBAPの歩き方
  • ハッキング・ラボのそだてかた〜ミジンコでもわかるBadUSB〜

組込み

  • Rust on Bare-metal Raspberry Pi
  • マイコンをハードモードで始めよう!
  • 組込みエンジニアの教科書

その他

  • 解説Core Utils

まとめ

簡単後払い: 12,700円
現金払い: 3,900円

まだ、買い忘れた感ありました。家計簿に入力したら怒られそうです!

Zephyrのテストフレームワーク調査③〜はじめてのZmock〜

はじめに

Zephyrにはテストフレームワーク (Mockもあります!) が用意されています。 テストは大事です。さっさとテストを作れるようにしましょう!

docs.zephyrproject.org

前回は、単純なテストを試したので、今回は、Mockを試してみます。

サンプル

とにかくサンプルを動かしてみます。

|> zephyr/tests/ztest/test/mock

$ mkdir build && cd $
$ cmake -GNinja -DBOARD=unit_testing ..
$ ninja run-test
Running test suite mock_framework_tests
===================================================================
starting test - test_parameter_tests
PASS - test_parameter_tests
===================================================================
starting test - test_return_value_tests
PASS - test_return_value_tests
===================================================================
starting test - test_multi_value_tests
PASS - test_multi_value_tests
===================================================================
Test suite mock_framework_tests succeeded
===================================================================
PROJECT EXECUTION SUCCESSFUL

そう言えば、BOARDがunit_testingかどうかでcmakeが分岐していました。 試しに、qemu_cortex_m3をターゲットにしてみます。

$ mkdir build && cd $
$ cmake -GNinja -DBOARD=qemu_cortex_m3 ..
...
This warning is for project developers.  Use -Wno-dev to suppress it.

CMake Warning at ../../../../CMakeLists.txt:1372 (message):
  

        ------------------------------------------------------------
        --- WARNING:  __ASSERT() statements are globally ENABLED ---
        --- The kernel will run more slowly and use more memory  ---
        ------------------------------------------------------------
...

なるほど。assertが有効になっているので遅いし、メモリを使う、という警告が出ます。

$ ninja run
[1/104] Preparing syscall dependency handling

[98/104] Linking C executable zephyr/zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:       12236 B       256 KB      4.67%
            SRAM:        4856 B        64 KB      7.41%
        IDT_LIST:           8 B         2 KB      0.39%
[103/104] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3
qemu-system-arm: warning: nic stellaris_enet.0 has no peer
***** Booting Zephyr OS 1.13.99 *****
Running test suite mock_framework_tests
===================================================================
starting test - test_parameter_tests
PASS - test_parameter_tests
===================================================================
starting test - test_return_value_tests
PASS - test_return_value_tests
===================================================================
starting test - test_multi_value_tests
PASS - test_multi_value_tests
===================================================================
Test suite mock_framework_tests succeeded
===================================================================
PROJECT EXECUTION SUCCESSFUL

unit_testingのときと違い、Zephyrがフルビルドされています。 いいですね。後で、実ボード上でも動くか試してみましょう。

CMakeLists.txt

普通のユニットテストと同じです。

cmake_minimum_required(VERSION 3.8.2)
if(BOARD STREQUAL unit_testing)
  list(APPEND SOURCES src/main.c)

  include($ENV{ZEPHYR_BASE}/tests/unit/unittest.cmake)
  project(mock)
else()
  include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
  project(mock)

  FILE(GLOB app_sources src/*.c)
  target_sources(app PRIVATE ${app_sources})
endif()

prj.conf

少し設定項目が増えます。

CONFIG_ZTEST=y
CONFIG_ZTEST_ASSERT_VERBOSE=1
CONFIG_ZTEST_MOCKING=y
CONFIG_ZTEST_PARAMETER_COUNT=5

CONFIG_ZTEST_MOCKINGは、Mockを有効にするために必要です。

CONFIG_ZTEST_PARAMETER_COUNTは、Mockで検証するパラメータおよび戻り値の数を制限します。デフォルトでは1のようです。

src/main.c

テストコードを書いていきます。

#include <ztest.h>

static int expect_two_parameters_and_return(int a, int b) {
    ztest_check_expected_value(a);
    ztest_check_expected_value(b);

    return ztest_get_return_value();
}

static void mock_test(void) {
    ztest_expect_value(expect_two_parameters_and_return, a, 1);
    ztest_expect_value(expect_two_parameters_and_return, b, 2);
    ztest_returns_value(expect_two_parameters_and_return, 3);

    zassert_equal(expect_two_parameters_and_return(1, 2), 3, NULL);
}

void test_main(void) {
    ztest_test_suite(my_first_mock,
        ztest_unit_test(mock_test)
    );

    ztest_run_test_suite(my_first_mock);
}

うわ、面倒くさいです…。

動作確認

無事動きます。

$ mkdir build && cd $_
$ cmake -GNinja -DBOARD=unit_testing ..
$ ninja run-test
Running test suite my_first_mock
===================================================================
starting test - mock_test
PASS - mock_test
===================================================================
Test suite my_first_mock succeeded
===================================================================
PROJECT EXECUTION SUCCESSFUL

感想

Mockの仕組みがあるだけでかなり嬉しいです。 が、ちょっと書き方が面倒ですね…。

Zephyrのテストフレームワーク調査②〜はじめてのZtest〜

はじめに

Zephyrにはテストフレームワーク (Mockもあります!) が用意されています。 テストは大事です。さっさとテストを作れるようにしましょう!

docs.zephyrproject.org

何事も写経から、ということで、サンプルの写経を行っていきます。

汎用テンプレート

まずは、公式にあるテンプレートを眺めます。

#include <ztest.h>

extern void test_sometest1(void);
extern void test_sometest2(void);
#ifndef CONFIG_WHATEVER              /* Conditionally skip test_sometest3 */
void test_sometest3(void)
{
     ztest_test_skip();
}
#else
extern void test_sometest3(void);
#endif
extern void test_sometest4(void);
...

void test_main(void)
{
     ztest_test_suite(common,
                         ztest_unit_test(test_sometest1),
                         ztest_unit_test(test_sometest2),
                         ztest_unit_test(test_sometest3),
                         ztest_unit_test(test_sometest4),
                );
     ztest_run_test_suite(common);
}

このソースコードは、sanitycheckスクリプトが解析するため、ztest_test_suiteの宣言は、以下のルールに従う必要があります。

  • 1行1宣言
  • 条件実行は、ztest_test_skip()を使用

ztest_test_suiteの中にifdefマクロがあったり、1行で2つ以上のテストケースは書けません。

CMakeLists.txt

まずは、cmakeを写経します。

cmake_minimum_required(VERSION 3.8.2)
if(BOARD STREQUAL unit_testing)
    list(APPEND SOURCES src/main.c)

    include($ENV{ZEPHYR_BASE}/tests/unit/unittest.cmake)
    project(base)
else()
    include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
    project(base)

    FILE(GLOB app_sources src/*.c)
    target_sources(app PRIVATE ${app_sources})
endif()

ん?BOARDがunit_testingかどうかで処理が分岐していますね。

試しにサンプルに戻って、-DBOARD=unit_testingで実験してみます。

# at zephyr/tests/ztest/test/base/
$ mkdir build && cd $_
$ cmake -GNinja -DBOARD=unit_testing ..
$ ninja
[5/5] Linking C executable testbinary

む、何やらtestbinaryなるバイナリが出てきました。

$ ./testbinary 
Running test suite framework_tests
===================================================================
starting test - test_empty_test
PASS - test_empty_test
===================================================================
starting test - test_assert_tests
PASS - test_assert_tests
===================================================================
Test suite framework_tests succeeded
===================================================================
PROJECT EXECUTION SUCCESSFUL

いけるやん!

build.ninjaを確認すると、run-testというコマンドがあります。

$ ninja run-test
Running test suite framework_tests
===================================================================
starting test - test_empty_test
PASS - test_empty_test
===================================================================
starting test - test_assert_tests
PASS - test_assert_tests
===================================================================
Test suite framework_tests succeeded
===================================================================
PROJECT EXECUTION SUCCESSFUL

いけるやん!

特定のユニットテストだけの実行であれば、高速にテストできます。

testcase.yaml

特に言うことはないですね。

tests:
  testing.ztest:
    tags: test_practice
    type: unit
  testing.ztest.verbose_0:
    extra_args: CONF_FILE=prj_verbose_0.conf
    tags: test_practice

prj.conf

最低限、CONFIG_ZTESTが必要です。

CONFIG_ZTEST=y

src/main.c

ありきたりなテストを書いてみます。

#include <ztest.h>

static void my_first_test(void) {
    zassert_true(1, NULL);
}

void test_main(void) {
    ztest_test_suite(my_first_tests,
        ztest_unit_test(my_first_test)
        );
    
    ztest_run_test_suite(my_first_tests);
}

zassert_true()API仕様は次のとおりです。

zassert_true(cond, msg, ...)
Assert that cond is true.

Parameters
cond: Condition to check
msg: Optional message to print if the assertion fails

オプショナルなメッセージが不要な場合は、NULLにするようです(こういうの好きではないですが)。

アサーション用のAPIはいくつか用意されています。

次は、Mockを試してみたいと思います。

Zephyrのテストフレームワーク調査①〜サンプル実験〜

はじめに

Zephyrにはテストフレームワーク (Mockもあります!) が用意されています。 テストは大事です。さっさとテストを作れるようにしましょう!

docs.zephyrproject.org

Ztest

ZephyrのテストフレームワークZtestは、インテグレーションテストにも、特定モジュールのユニットテストにも利用できます。

The framework can be used in two ways, either as a generic framework for integration testing, or for unit testing specific modules.

サンプルのテストを動かしてみる

zephyr/tests/ztest/test/baseに簡単なサンプルが見つかりました。

$ ls
CMakeLists.txt      prj_verbose_1.conf  src
prj_verbose_0.conf  prj_verbose_2.conf  testcase.yaml

testcase.yamlにテストケースを書いて、その際にプロジェクトの設定を切り替えることができるようです。

$ cat testcase.yaml 
tests:
  testing.ztest:
    tags: test_framework
    type: unit
  testing.ztest.verbose_0:
    extra_args: CONF_FILE=prj_verbose_0.conf
    tags: test_framework
  testing.ztest.verbose_1:
    extra_args: CONF_FILE=prj_verbose_1.conf
    tags: test_framework
  testing.ztest.verbose_2:
    extra_args: CONF_FILE=prj_verbose_2.conf
    tags: test_framework

テストを実行するには、zephyr/script/sanitycheckを使います。

# テストケースを指定してテストを実行
$ ./scripts/sanitycheck -s <PATH TO TEST>/<test-identifier>

# プロジェクトのテストを全て実装
$ ./scripts/sanitycheck -T <PATH TO TEST>/

試しに、testing.ztest.verbose_0をターゲットにテストをしてみます。

$ ./scripts/sanitycheck -s tests/ztest/test/base/testing.ztest.verbose_0
JOBS: 16
Selecting default platforms per test case
Building testcase defconfigs...
10 tests selected, 81753 tests discarded due to filters
total complete:   10/  10  100%  failed:    0
10 of 10 tests passed with 0 warnings in 15 seconds

おっそ!15秒ですか…。

それは、ともかくとして、何をやっているのか全くわかりません。少しずつ紐解いてみましょう。

サンプルプロジェクトの中身を覗く

テストケースの記述は、至って普通です。

|> src/main.c

#include <ztest.h>

static void test_empty_test(void)
{
}

static void test_assert_tests(void)
{
    zassert_true(1, NULL);
    zassert_false(0, NULL);
    zassert_is_null(NULL, NULL);
    zassert_not_null("foo", NULL);
    zassert_equal(1, 1, NULL);
    zassert_equal_ptr(NULL, NULL, NULL);
}

void test_main(void)
{
    ztest_test_suite(framework_tests,
             ztest_unit_test(test_empty_test),
             ztest_unit_test(test_assert_tests)
             );

    ztest_run_test_suite(framework_tests);
}

どうも、sanitycheckスクリプトで解析するようで、ドキュメントに制限が色々と書いてあります。 それは、追々紹介します。

今日は、とりあえずここまでです。