2018/06/30

[github]古いgistのURLを知りたい

ちょっとしたソースコードを載せるのに、githubのgistをしばしば使っている。
便利なのだが、編集すると、その前の内容を確認するのが難しい。
いや、差分はわかるのだけど、githubのcommit historyみたいに、その時の内容だけが見たいのだ。


どうやら、その時のハッシュが分かれば載せられるらしい。
例えば、これはzlibサンプルのrevision1のURLだ。

https://gist.github.com/hirokuma/0f038adc04c1ec38ba6c01f8b2db4fb5/4b920d2c12d7893c23a93343cd74d116de731077

URLの最後がハッシュ値なのだが、どうやって取ってくれば良いのだろう?


・・・というのを、長々と調べていた。。。
いや、なに、"Revisions"というタブがあるのは知っていたよ。
そこで差分も出てきているのは知ってる。


ただ、そこに全Revisionの差分が出ているということにまでは気がつかなかったのだ。
下の方にスクロールさせると、過去の差分があるし、Viewボタンまで付いている。
Viewをクリックすれば、そのときのURLが出てくるのだな。


はは、ははは。
私は乾いた笑いを飲み込むしかなかった。

[c/c++]zlibを使ってみよう

zlibを使わなくてはならなくなった。
名前はよく聞く・・・。
リンクするときに「-lz」と短く済むので、印象に残っている。

よく使われているので、使い方も難しくは無いだろう。


WSLのUbuntu 16.04で試す。

sudo apt install zlib1g-dev


zlibのマニュアルは、こちら。

zlib 1.2.11 Manual https://zlib.net/manual.html


ただ・・・圧縮と解凍さえ分かればよいので、使い方だけ見ておこう。
やりたいのは、ファイルではなくRAMで持っているデータなので、そこは読み替えていく。

zlib Usage Example https://zlib.net/zlib_how.html

https://zlib.net/zpipe.c


まず、テストデータを作ろう。
xxdというコマンドを使うと、ファイルをC言語の配列形式にしてくれる。
便利だ・・・。

xxd -i hirokuma.png > hirokuma.h


Exampleでは、CHUNKサイズのバッファin[]とout[]を準備している。
def()が圧縮、inf()が展開。
どちらも、二重のdo-while()になっている。

まず、def()から。
外側のdo-while()は、ファイルをCHUNKずつ読み取るためのループ。
内側のdo-while()は、deflate()を実行して、avail_outが0になっている間はループする。
「keeps calling deflate() until it is done producing output」と書いてあるので、avail_outが0ということは処理が継続しているという意味なのだろう。

圧縮中、deflate()の引数はZ_NO_FLUSHで、最後だけZ_FINISHにする。
ストリーミングで圧縮するから、ある程度データが揃うまで吐き出さないのだろうね。

簡単なサンプルを作った。
圧縮のみで、圧縮しているデータは、うちのアイコン(PNG)だ。
PNG画像もzlibで圧縮されているようだが、気にするまい。

https://gist.github.com/hirokuma/0f038adc04c1ec38ba6c01f8b2db4fb5/4b920d2c12d7893c23a93343cd74d116de731077

image

まあ、データは何でもよいのだが、内側のdo-while()がどういう周り方をするのかが気になった。

$ gcc -o tst zlib_comp.c -lz
$ ./tst
p=0x6010a0
   avail_out=1022
p=0x6014a0
p=0x6014a0
   avail_out=1024
p=0x6018a0
p=0x6018a0
   avail_out=1024
p=0x601ca0
p=0x601ca0
   avail_out=1024
p=0x6020a0
p=0x6020a0
   avail_out=1024
p=0x6024a0
p=0x6024a0
   avail_out=1024
p=0x6028a0
p=0x6028a0
   avail_out=1024
p=0x602ca0
p=0x602ca0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=0
   avail_out=103
p=0x603030

next_inは自動的に進むようだから、全データがRAM上にあるなら、初期化だけしておけばよさそうだ。


内側のループが回ったのは、最後だけだ。
何かルールがあるのだろうか?
もし最後だけなら、通常は内側のdo-while無しでもよいのかもしれんが、これだけじゃわからんな。


というわけで、マニュアルを見てみよう。
けっこう、見づらい・・・。

https://zlib.net/manual.html#Basic

If deflate returns with avail_out == 0, this function must be called again with the same value of the flush parameter and more output space (updated avail_out), until the flush is complete (deflate returns with non-zero avail_out).

In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that avail_out is greater than six to avoid repeated flush markers due to avail_out == 0 on return.

avail_outが0の場合は、flushオプションを同じ状態で呼び続けなくてはならん。
ということは、Z_FINISHの場合だけではないということか。


"more output space (updated avail_out)"と書いてあるが、サンプルではavail_outを再設定しているだけで、out[]のサイズは同じだな。
そもそも、fwirte()するサイズは「CHUNK - strm.avail_out」だから、8KBくらいのデータでは外側のループで回っているときはout[]がほぼ出力されず、最後にZ_FINISHしたときに書き込まれていることになる。


flushオプションもいくつか種類があるようだが、圧縮したデータそのものがほしい場合はサンプル通りにやっておけばよいんじゃ無かろうかね。


展開する方も、同じようにやった。

https://gist.github.com/hirokuma/0f038adc04c1ec38ba6c01f8b2db4fb5/2ae53cdeb15bc6e79ed4c65474040a3327b5dbad

$ ./tst
exec: comp
retval=1
exec: decomp
retval=-5
retval=-5
retval=-5
retval=-5
retval=-5
retval=-5
retval=1

追加したのは、decomp()。
-5は、Z_BUF_ERROR

サンプルではエラー処理していないし、最後に1(Z_STREAM_END)が出るところから見ると、普通なんだろう。

Z_BUF_ERROR if no progress was possible or if there was not enough room in the output buffer when Z_FINISH is used.

Note that Z_BUF_ERROR is not fatal, and inflate() can be called again with more input and more output space to continue decompressing.


APIに何があるのか読みづらかったので、こちらの解説記事の方がよさそう。
http://s-yata.jp/docs/zlib/

compress()なんてあったのか・・・。
https://zlib.net/manual.html#Utility

2018/06/24

[excel]自由に線を引きたい

たまにはExcelのことを書こう。


私がExcelを使うのは、文字と画像をまぜこぜにして、なおかつ広いスペースが使えるからだ。
画像も、画像ファイルだけでなく、線画を描くこともできる。


よく使うのは、曲線コネクタだ。

image

直線だと他の図形とぶつかるけど、曲線だと迂回させることもできる。


できるのだが、あんまり自由度がないのが悩みどころだ。
黄色のマーカーを動かすと、少しは形が変わるのだが、そんなに大幅に変わらない。

image image


自分で使う図ならいいのだが、たまにはきれいな形で人に見せたいこともある。
ああ、大きく迂回させたい・・・。


どうしてもExcelを使って、迂回したような線を引きたい場合は、こういう技もある。

image


何のことはない、図形と直接コネクトさせるのではなく、別の図形にコネクトさせてしまうのだ。
ここでは、円にコネクトさせている。

image


円じゃなくて、四角形の方がうまくいくかも?
この辺は、倍率で見た目が変わったり、線の太さで影響がありそうなので、あれこれやってみるのがよかろう。

image

ただ、中継点のところが薄くなってしまうようだ。
image


曲線を使えば期待した線を描きやすいのだけど、あれは後から変更できないみたいだし。
うーむ。


他のお絵かきツールを探していると、draw.ioというものが紹介されていた。

https://www.draw.io/

image

ふむ、これは自由度が高い。
やはりExcelっぽくない絵にしたいときは、Excelじゃないツールを使うのがよいな。

2018/06/17

[wireshark]npcapを使う

Windows版のWireSharkを久々に起動させ、最新版にした。
そのせいかどうかわからないが、起動しない。。。
プログレスバーの途中、pluginsがどうのこうのというところまで文字が出て、そこから進まない。

アンインストールしてやり直したり、ユーザデータを消したりしたが、関係なさそう。


https://osqa-ask.wireshark.org/questions/48178/wireshark-fails-to-start-on-windows-10

WinPcapだと動かない人もいるみたいだから、npcapにしてみるといいかもね、というところか。

WinPcapごとアンインストールし、npcapのWindowsインストーラを https://nmap.org/npcap/ から探して(今は0.99-r6が最新のようだ)、インストールしてPCを再起動。

そうすると、WireSharkが起動した。


うまくWinPcapでうまくいかないときにPC再起動はさせていないので、もしかしたら再起動だけで直ったかもしれない。
まあ、npcapだとWindowsでもlocalhostを見ることができるから、ちょうどよいだろう。

2018/06/09

[intellij]gitignoreをどうするのか(2018/06/09)

3箇所にあるPCで、Javaの開発・・・・というほどでもないことをやっている。
どのPCで作業するかわからないので、GitHubにプロジェクトを突っ込んでいるだけだ。

2台はIntelliJ、1台はAndroid Studioを使っている。
Android Studioである必要はないのだが、IntelliJとAndroid Studioをわざわざインストールするのもなぁ、ということで、試しにやっている程度だ。


C/C++のイメージでやっていたので、ソースファイルをgitに載せていればなんとかなるんだろう、と思っていたのだが、そうではないらしい。
とにかく、どの環境で作業しても、しょっぱなからgitに差分があるのだ。
ソースファイルだったらよいのだけど、設定ファイルが変わっている。
変わるというよりは、ファイルごと削除になっていることが多い。
作業するディレクトリが違うからかと思ったのだが、どうもそれだけでもなさそう。


腹が立ってきたので、調べることにした。


まずは、直近の記事を検索。

JetBrains IDE(IntelliJ IDEAなど)を使う時のgitignoreについて
https://qiita.com/mokrai/items/1f668dd0d1bee15b56f0

記事の中のリンクに、JetBrainsの記事がある。
Updatedが2018/06/08なので、定期的に更新されるページなのだろう。


「.idea」というディレクトリがあるかどうかで、対応が変わるらしい。
うちのにはあるので、そっちを読む。

  • .ideaディレクトリの中の全部だけど、workspace.xmlとtasks.xmlは除外する
  • 拡張子が.imlのもの(IntelliJ IDEAが反映したもの : applies to IntelliJ IDEA)

意外と、シンプルな方針だ。
Qiitaの人が読んだときと、まだ変わっていないようだ。


それ以外に「Be careful」と「not to shared」も書いてある。
「Be careful」はパスワード関連のようだからよいとして、「not to shared」だ。

  • 拡張子が.imlのもの(GradleやMavenがベースになったプロジェクト)
  • gradle.xml
  • dictionaries
  • .idea/libraries以下にある拡張子が.xmlのファイル(GradleやMavenが生成したもの)


まてやー!
.imlでも、共有したりしなかったりがあるのかぁぁぁぁ。


これ、ちょっと、無理じゃないかい?
今の時点でGitHubのJetBrains gitignore例は2018/May/10で、JetBrainsの記事はJune/08だから、まだそっちには反映されていないのかもしれないし、今のままでもよいのかもしれない(QAを全部読む気力は無いのだ)。
読み間違えているかもしれんので、画像を残しておこう。


image


で、だ。


いま、別の場所のIntelliJで作業して、commit / pushしたものを、また別の場所で開いている。
こちらもIntelliJだ。

.idea/libraries/*.xmlは、書いてあるとおりに除外されている。
除外というか、削除されたことになっている。
.gitignoreの「.idea/**/libraries」が効いているのだろうか?
いや、私はその行を削除していたな。


ただ、JetBrainsが言う通りに共有されていないにしても、どうしてよいのか困る。
プロジェクトの設定でDependenciesを見ると、そいつらが名前だけ載っていて、赤文字になっているのだ。
いかにも「そのファイルはありません」みたいな感じで、ビルドも失敗する。
復活させればビルドできるから、その行をコメントアウトしたこと自体がダメだったのか。


なんとなくだが、変なことをしていなければ、Mavenだか何だか知らないが、持っていないライブラリは勝手にダウンロードしてくれているようだ。
プロジェクトを開いたときに妙に時間がかかることがあるから、あれはそうなんだろう。



今月はまだJavaを扱わないといけないようだから、急いで結論を出すことはあるまい。

[java][ubuntu]bitcoinjをコンソールでビルドしてみよう

bitcoinjをビルドしようとした。
環境としてJDKはインストールされていて、Android Studioもあったので、それからできるだろうと思った。

が、protobufのコンパイラが入っていないので、途中でエラーになってしまった。
Windows向けのprotocもありそうだったが、そこまでしてビルドしたいという気分でもない(なんだそりゃ?)。

READMEを見ると、Gradleでビルドするのが普通のようなのだ。
かといって、Windows環境にGradleだけインストールしたいという気分でもない。
Android Studioのところからパスを通せば良いのだろうが、結局はprotobufがいるだろう。


ごにょごにょ書いたが、Azure上にUbuntu 16.04 VMを立てているので、そっちでビルドできるようにしておいた方が後々いいんじゃなかろうかということで、やってみよう。


Gradleのインストール方法を見ると、SDKMAN!というものをインストールするのが楽らしい。
うちのUbuntuにzipやunzipが入っていなかったので、curlするスクリプトがエラーを返してくれた。
足りないものを入れていけばインストールできるだろう。

sudo apt install openjdk-8-jdk-headless zip unzip (他にいるもの...)
curl -s "https://get.sdkman.io" | bash
source "/home/xxx/.sdkman/bin/sdkman-init.sh"
sdk install gradle 4.8


protobufはaptでもいけそうだが、後々使いそうな気もするので、今の時点で最新のv3.5.1から持ってこよう。

mkdir protobuf
cd protobuf
wget https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip
unzip protoc-3.5.1-linux-x86_64.zip
export PATH=$HOME/protobuf/bin


protocにパスが通っているのを確認したら、bitcoinjをビルドしよう。

git clone https://github.com/bitcoinj/bitcoinj.git
cd bitcoinj
gradle clean build
....

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':wallettemplate:compileJava'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 1m 56s
24 actionable tasks: 21 executed, 3 up-to-date


エラーで終わってしまった。。。
が、findでjarファイルを探すとできていたから、いいや。


これを書いている時点での最新リリースバージョンはv0.14.7だが、masterはそれよりも進んでいる。
構成に変更があるようで、例えばorg.bitcoinj.core.ScriptExceptionorg.bitcoinj.script.ScriptExceptionになっていた。

まあ、私も「使い始めました」という程度なので、何がどう変わったのかという以前に、使い方を覚えねば。。。
しかし、ExamplesのawaitRunning()が動かないので、最新版は私には無理かもしれんな。

・・・guavaのバージョンが古かっただけみたい。
しくみが、しくみが分かりません。。。

[win10][office]PNGのサムネイルが表示されない(未解決)

会社で使っているWindows10だが、Excelでファイル選択して画像を貼り付けたいことがある。
その際、ファイル選択するダイアログが表示されるのだが、画像ファイルのサムネイルが表示されない。
以前は気にしていなかったので、何かの際にそうなってしまったようだ。


Explorerの設定をいじったのかな、と思ったが、そうでもない。
そして、Excelだけでなく、Wordもダメだった。
かといって、全部がダメになったわけではない。ダメなのはOffice系だけのようだ。


不思議なことに、一度Explorerでサムネイルを表示させたファイルであれば、Officeでもサムネイルが表示されている。
キャッシュされたものであればOKなようだ。


ネットで検索して、現象として一番近いのはこちら。
Office系だけPNGサムネイルが表示されないのだが、解決案が「別のアカウントを使う」らしい。

https://answers.microsoft.com/ja-jp/windows/forum/windows_10-files/png%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE/3ae450be-e0c4-4fa1-a3b1-3de68f41ea4e

いやー、ちょっと待ってくれー、といいたい。
特定は難しいのかもしれないけど、多少は代替え案を出してほしいところだ。


この問題の難しいところは、Explorerの設定でサムネイルを表示させないようにできることだ。
検索すると、それが大量に引っかかってしまい、今回の現象にたどり着けないのだ。
上記で質問された方も、さんざん検索して見つからずに質問されたのではなかろうか。



日本語の検索だとらちが明かないので、英語で探してみた。
https://answers.microsoft.com/en-us/windows/forum/windows_10-files/some-jpg-and-png-thumbnails-disappeared/d2b83204-a38f-4fdd-aa32-74481923f4a7


これを書いているPCでは試せないので、次回会社に行ったときにやってみよう。


2018/06/02

[bitcoij]IntelliJでbitcoinjを使ってみよう

「唐突」の「唐」は、隋の次の唐だろうか?
隋の時代が長いこと続いていたのに、いきなり唐の時代に突入したとか、そんなことだろうか。


というわけで、今回は唐突にbitcoinjを使ってみようと思う。
どちらかといえば、IntelliJの設定方法を忘れそうなので、それをメモとして残しておきたいだけなのだがね。


まず、IntelliJと、JDK8をインストールする。
環境変数JAVA_HOMEなんかも設定しないといかんのかもしれんな。
今回は、jdk 1.8.0 172だ。


IntelliJの設定も、適当にやっておこう。
うちのWindows環境では年に数回しかIntelliJを起動しないので、起動するたびに最新版へのアップデートがいる。
今回は、2018.1だ。



当然、IntelliJの使い方も把握していないのだが、雰囲気でやっておこう。

まず、新規でプロジェクトを作る。
テンプレートからコンソールを選択しておく。


本題であるbitcoinjを追加。
どうやるのが簡単なのか分からんが、「File > Project Structure ...」でModulesを選び、Dependenciesタブから追加した。

  1. 右側の「+」をクリック
  2. 「2. Library...」「From Maven...」
  3. 出てきたダイアログから検索できるので、bitcoinj-coreを検索して、追加
  4. 同じようにして、slf4j-nopを検索して、追加

image

slf4j-nopではなく、slf4j-simpleでもよい。
デバッグ時にはそっちの方が良いのかもしれん。
両方置いておくと、このリストで上にある方が選ばれるようだ。
ただ「こっちを選んだ」みたいなログが出てうっとうしいので、片方だけ見えるようにしておいた方が良かろう。
Scopeを「Test」にしたのは、後でまた検索し直すのが面倒だったので、リストに置いておくためだ。


最後に、適当にコードを書いて実行。
というと、がんばって調べたように見えるが、こちらを読んだだけだ。

bitcoinjの入門 - Bitcoin blog
http://adrenaline2017.hatenablog.com/entry/2017/11/23/000000


オリジナルは、本家のサイトだろうが、日本語になっているだけでありがたい。

Getting started in Java
https://bitcoinj.github.io/getting-started-java

01: package com.hiro99ma.blogpost.bitcoinj_test01; 02: 03: import org.bitcoinj.core.Address; 04: import org.bitcoinj.core.ECKey; 05: import org.bitcoinj.core.NetworkParameters; 06: import org.bitcoinj.kits.WalletAppKit; 07: import org.bitcoinj.params.TestNet3Params; 08: 09: import java.io.File; 10: 11: class BitcoinjTest { 12: private NetworkParameters params; 13: private WalletAppKit wak; 14: 15: BitcoinjTest() { 16: params = TestNet3Params.get(); 17: wak = new WalletAppKit(params, new File("./wallet"), "wak") { 18: @Override 19: protected void onSetupCompleted() { 20: if (wallet().getKeyChainGroupSize() < 1) { 21: wallet().importKey(new ECKey()); 22: } 23: } 24: }; 25: wak.startAsync(); 26: } 27: 28: void exec() { 29: wak.awaitRunning(); 30: Address addr = wak.wallet().currentReceiveAddress(); 31: System.out.println("addr = " + addr.toString()); 32: } 33: } 34: 35: public class Main { 36: public static void main(String[] args) { 37: BitcoinjTest bjt = new BitcoinjTest(); 38: bjt.exec(); 39: } 40: }


着金アドレスをコンソールに出力させるだけだ。
あれこれやりたかったら、Examplesを見るのが早いだろう。
IntelliJの設定が分かったので、私は満足した。。。

[lmdb][win10]もう少しlmdbがうまく動作しない件を調査

以前、こういう記事を書いた。


hiro99ma blog: [lmdb][win10]Windows10 1803でWSLのファイル書込みがうまく動いてない?(2018/05/05)
https://hiro99ma.blogspot.com/2018/05/lmdbwin10windows10-1803wsl20180505.html


今日(2018/06/02)もまだ発生している。
Ubuntu 18.04も試してみたが、同じだ。
だから、これはOSよりもWSL側に何かあるんじゃなかろうかと考えた。

解決できないだろうけど、せっかくなので、もう少し調べてみよう。


前回からgistの内容を変更しているので、再掲しておこう(Revision 3)。

https://gist.github.com/hirokuma/4ea889b2a596c59ef4f862e9df54e469


これを実行させると、初回から3秒くらいまでの間はL.95でエラーが出力されている。

test1(): OK
ERR(95): MDB_BAD_TXN: Transaction must abort, has a child, or is invalid

前回のブログではmdb_put()でエラーになったと書いているが、mdb_get()だな。
勘違いして書いたのか、現象が変わったのか・・・。
mdb_put()がうまくいっていないから、mdb_get()に失敗したといいたかったのかもしれんが、よくわからんな。


まあ、よかろう。
mdb_get()で失敗しているのだ。


まず、実行するディレクトリを変えてみた。
前回動かしたときは、Windowsで作業しているディレクトリだったのだ。
もしかすると。WSLで管理しているディレクトリじゃないとうまくいかない、とかあるのかもしれん。

が、これは関係なさそうだった。
「~/」に移動させて実行したが、結果は同じだ。



次は、失敗したときと成功した時をstraceで眺めた。

失敗時の出力。
どのくらい抜粋すればよいかわからないので、いいところが載ってないかも。

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7f2044f1d000
open("testdb/data.mdb", O_RDWR|O_CREAT, 0644) = 4
fstatfs(4, {f_type=0x53464846, f_bsize=4096, f_blocks=58348607, f_bfree=35725114, f_bavail=35725114, f_files=999, f_ffree=1000000, f_fsid={1, 0}, f_namelen=255, f_frsize=4096, f_flags=1056}) = 0
pread64(4, "\0\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 0) = 152
pread64(4, "\1\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 4096) = 152
mmap(NULL, 1048576, PROT_READ, MAP_SHARED, 4, 0) = 0x7f20440e0000
open("testdb/data.mdb", O_RDWR|O_DSYNC) = 5
fcntl(3, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=1}) = 0
write(2, "test1(): OK\n", 12test1(): OK
)           = 12
lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\22\0\330\17\330\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "ERR(95): MDB_BAD_TXN: Transactio"..., 73ERR(95): MDB_BAD_TXN: Transaction must abort, has a child, or is invalid
) = 73


成功時。

mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7f4d72ec0000
open("testdb/data.mdb", O_RDWR|O_CREAT, 0644) = 4
fstatfs(4, {f_type=0x53464846, f_bsize=4096, f_blocks=244189951, f_bfree=103944675, f_bavail=103944675, f_files=999, f_ffree=1000000, f_fsid={1, 0}, f_namelen=255, f_frsize=4096, f_flags=1056}) = 0
pread64(4, "\0\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 0) = 152
pread64(4, "\1\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 4096) = 152
mmap(NULL, 1048576, PROT_READ, MAP_SHARED, 4, 0) = 0x7f4d720e0000
open("testdb/data.mdb", O_RDWR|O_DSYNC) = 5
fcntl(3, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=1}) = 0
write(2, "test1(): OK\n", 12test1(): OK
)           = 12
lseek(4, 8192, SEEK_SET)                = 8192
writev(4, [{"\2\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\3\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 2) = 8192
pwrite64(4, "\n\0\0\0\0\0\0\0\0\0\2\0\24\0\240\17\320\17\240\17\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096, 40960) = 4096
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "test2(): OK\n", 12test2(): OK
)           = 12


失敗straceログは、2回目の実行を載せている。
初回だとディレクトリ作成やファイル作成に違いが出てきてわかりにくいからだ。

文字色を付けたところが、コンソールログ("write"で始まる行)。
背景色を付けたところは、違いがありそうな箇所。


失敗時は、ファイル4へのpwrite64()が発生していない。
fd=4は、最初に開いた方のtestdb/data.mdbだ。。。あれ、fd=5もtestdb/data.mdbだな。
同じファイルをオープンしているのか?

2回目のopen()にはO_DSYNCが指定されていた。
「ファイルに対する書き込み操作は、同期 I/O のデータ完全性完了の要件に基づいて行われる」らしい。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/open.2.html


・・・などと、違いがあるところばかり気にしていたが、test1()でOKを出力させる前のところでファイル書込みをしてないよね?
mdb_put()に相当する処理がスキップされている??
既にデータがあるから、書き込む前にデータを比較して、同じだからスキップされたのかもしれない。
まあ、ストレージへの書込みはコストが高いので、さもありなん。


そしてもう1つ。
成功する状態でいくつかstraceを取ってみたが、lseek()されるサイズは16384の場合もあったし、違うサイズの場合もあった。
その際、背景色が緑の出力は行われていなかった。。。


何が起きているのか、さっぱりわからんな。


WSLではないLinuxでのstraceを見よう。


mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7f10690bc000
open("testdb/data.mdb", O_RDWR|O_CREAT, 0644) = 4
fstatfs(4, {f_type="EXT2_SUPER_MAGIC", f_bsize=4096, f_blocks=7607162, f_bfree=594658, f_bavail=590562, f_files=3840000, f_ffree=2984847, f_fsid={-2044918571, 1650642210}, f_namelen=255, f_frsize=4096, f_flags=4128}) = 0
uname({sysname="Linux", nodename="lntest1", ...}) = 0
pread64(4, "\0\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 0) = 152
pread64(4, "\1\0\0\0\0\0\0\0\0\0\10\0\0\0\0\0\336\300\357\276\1\0\0\0\0\0\0\0\0\0\0\0"..., 152, 4096) = 152
mmap(NULL, 1048576, PROT_READ, MAP_SHARED, 4, 0) = 0x7f106839c000
open("testdb/data.mdb", O_RDWR|O_DSYNC) = 5
fcntl(3, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=0, l_len=1}) = 0
write(2, "test1(): OK\n", 12test1(): OK
)           = 12
lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\22\0\330\17\330\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "test2(): OK\n", 12test2(): OK
)           = 12


fd=4へのpwrite64()呼び出しは、起きたり起きなかったりで、これはWSLと同じだ。
連続して実行するとpwrite64()が呼び出されないようなので、キャッシュに吐き出されるまでは呼び出していないのかもしれん。


WSLで失敗したとき。

lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\22\0\330\17\330\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "ERR(95): MDB_BAD_TXN: Transactio"..., 73ERR(95): MDB_BAD_TXN: Transaction must abort, has a child, or is invalid
) = 73


Linuxで成功したときの例。

lseek(4, 16384, SEEK_SET)               = 16384
writev(4, [{"\4\0\0\0\0\0\0\0\0\0\2\0\22\0\302\17\302\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\5\0\0\0\0\0\0\0\0\0\2\0\22\0\356\17\356\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}, {"\6\0\0\0\0\0\0\0\0\0\2\0\24\0\240\17\320\17\240\17\0\0\0\0\0\0\0\0\0\0\0\0"..., 4096}], 3) = 12288
fdatasync(4)                            = 0
pwrite64(5, "\0\0\20\0\0\0\0\0\0\20\0\0\10\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 120, 32) = 120
write(2, "test2(): OK\n", 12test2(): OK
)           = 12


lseek()の結果と、pwrite64()の一番最後にある引数は毎回変わるので、Linuxの方は何度か実行して同じような出力を探した。


つまり、straceの結果からでは違いが分からんということだ。


まだだ・・・今のわしには力が足りんのだ。。。