2017年07月06日

AWStatsのインターフェースをいじろう(というのはお薦めできない)

結果として面倒だったので、「表示を変えた箇所を調べておけばバージョン変わってもなんとかなるかな?」くらいに考えている人は参考にならないとだけ言っておく。

AWStatsはログ解析ツールとしては結構有名どころらしく、普通にパッケージ管理ソフトで入るため、単に使いたいと言う場合にはあまり苦労することもなく導入できる。
また、使い方も調べればそれなりにでてくるため、さほど問題は起こらない。(少なくともここに来るような人にはすぐ見つけられるはず)
しかし、インターフェースは微妙なところもあり、変更したいと思ったりもする。

もっとも、やってみた感想は「やめた方がいい」なのだけど。

変更内容は、一年前の情報も同時に表示すること。
自分の所属が大学であるため、webサーバーの負荷などは大体月と曜日に依存している。
そのため、何か変化や異常があると、昨年と比較できれば割と簡単に見えるのである。
しかし、AWStatsのインターフェースはなぜかその年の情報しか出さないので、比較が難しい。
(また、その年しか出さないので、1-3月あたりでは表示される情報がなさすぎて使いづらい)

じゃあ並べるようにすればいいんじゃないかといじり始めたが、どうにもこうにも・・・
起こった問題(怒った問題)を挙げていこうと思う。

1)月ごとの情報を表示する箇所は割と簡単に見つけられて、HTMLMainMonthlyという関数になっている。
この中に前の年のデータを表示するルーチンを無理矢理書き込めばいいはずなのだが、全く部品化されていないので、今見ている年の処理をコピペするしかない。やれやれ。

2)よく見るとところどころにコメントアウトされたあからさまに不要とおぼしきコードが見える。バージョン管理してるだろうが。消せ。

3)無理矢理表示を追加してるうちに、月ごとの関数から移って日ごとの情報を表示する関数が目に入るが、インデントが少し壊れてる。直せ。(もっとも、自分が見たバージョンは現在のバージョンよりさらにひどかったので、少しずつ直しているのだろうか。全部直せよとは思うが。)

4)表示させるように変更したので、一年分の情報は読んでいるが全てのログを見ているわけではないということにすぐ気づく。サーバーによってはログがすごい量なのだから当たり前ではあるが、どこで読んでるのか分かりづらい。表示に使っているデータの変更箇所を追うと、どうやら3330行くらいからのRead_History_With_TmpUpdateという関数で読んでるらしい。呼び出し箇所を見ると月ごとに読むようにしている。ただし、選択した月とそれ以外で月以外にもう一つ引数が異なっている。意味はさっぱりわからず。中を見れば分かるかと関数の範囲を見ると3800行ある。読めるかこんなもの。

5)とりあえず選択していない月と同じように読ませればなんとかなるかとやってみたところ、前の年の表示に成功。一安心かと思ったら明らかに「時間毎の通信量」が増加している。何が起こってるのかと調べると昨年の同月分を単純に足している様子(1年後を表示させて気づいた)。変数から当たりを付けてRead_History云々を見ると「選択した月の情報なら計算対象にして、他の月だったら無視する」という状態になっている。月だけ??年のチェックは???

6)努力の甲斐あって表示に成功。ただ日本語表示になっているのにところどころ「日 月」の順になっていてもやもやする。かと思ったら一箇所だけ「〜年〜月〜」(日付はあるが「日」がない)になっていたりする。さらに気持ち悪い。何かと思って表示部を見たら順番がハードコーディングされている。一箇所だけ別ファイルからフォーマットを読んで出力している(ただし微妙に不足している)。だからその中途半端なのをやめろというのに。というか多言語対応やってるんならハードコーディングするなよ。

とまあこんな感じである。
全部手で直したので、当然のことながらバージョンアップしたらおじゃん。
全体的に思うのは、
1)全体のメンテナンスに気を遣っているか若干怪しい。
2)細かい引数指定があるのに、普通に出てくるはずの組み合わせを想定していない。
3)大域変数だらけで関数毎に想定している状態がさっぱりわからない。
4)関数がでかすぎる。
全体のテスト以外してないよねこれ、という感じである。
posted by chiguri at 01:30| Comment(0) | 雑多

2016年12月10日

cygwinの依存関係を調べよう

パッケージ削ると動かなくなるとかヒドすぎる。
インストーラが依存関係解析してるんじゃないのか。

などという愚痴が出た準備室で新しいお仕事である。
「なんかcygwinのインストーラがチェックする依存関係、かなり怪しい」という考えの下、提供されているツールからある程度依存関係を調べられないかということを相談していた。

で、cygcheckとlddという二つのコマンドで対処することにした。
1: exeとdllをディレクトリから漁り、どのパッケージ由来かをcygcheckで調べる
2: パッケージに現れるexeとdllをlddにかけて、依存するライブラリを調べる。そのライブラリが所属するパッケージへの依存関係が発生する。

まあ出来ないものじゃないし、とPerlでスクリプトを書いてやってみたのだが、cygcheckがやばいくらい遅い。
どうもcygcheckはパッケージ一覧とファイル一覧を持っていて、パッケージ由来は逆引きをやっているっぽいのだ。
多分ボトルネックになってるのは、HDDだからか、ファイルがバカみたいに増えて単に逆引き自体が遅いか(フルインストールなので60GBを越えている)、のどちらかだろう。
まあどちらにせよ解消しないといけない問題なので、別の方法で解決しようと考え中。

パッケージ一覧から作れば逆引きできるよねえ、Perlなんだし。
posted by chiguri at 00:15| Comment(0) | PC

2016年12月02日

MSetsの愚痴、あるいはTheorem Prover Advent Calendar 2日目

これはTheorem Prover Advent Calendar 2日目の記事です。

この記事は、Coqの標準ライブラリに含まれるMSetsについてです。
今回は利用側の視点ではなく、ライブラリ作成側の視点なので、まあ非常にニッチだと思います。

名前からピンとくるかもしれませんが、MSetsは集合のライブラリです。
集合に入っているかを判定する述語、要素の追加や削除、和集合や共通集合、フィルターをかけるなど、一通りのことができます。
ただし、単純な集合ではなく、別途利用者が定義した同値関係を与えることで、「同値な要素」が入っているかを判定するようになっています。
(同値関係にLogicに定義されているeqを使えばよくある集合になります)

MSetsでは、新しい実装を実現する方法として、直接インターフェースを満たすように定義する以外に、RawSetsと呼ばれる「それ自体は集合らしくないこともありうるが、条件を付けると性質が成り立つ」ように記述するインターフェースが提供されています。
(実際はWSetsに対してWRawSets、Setsに対してRawSetsがあります。前者が要素に順序関係を必要としない、後者が要素に順序関係を必要とするものです。以下の説明は正しくはWRawSetsのものです。)
ちょっと分かりにくい日本語だと思うので、例としてリストを使った実装を考えます。
集合を実現するためにリストを用いることは不自然ではありませんが、リストでは要素の重複があったりします。
MSetsでは集合の要素全てをリストにして取り出すelementsという関数が定義されており、そのリストに重複はないことが要求されているので、重複を認めるとちょっと面倒です(ちょっとだけですけどね)。
そのため、「重複のないリストである」場合にそれを集合とする、と考えることで、リストを使ってRawSetsを構築できます。
インターフェース上の性質は全て「重複のないリストであるとする」という条件を課すことになり、それによって初めて証明が可能になったり、また簡単になったりします。

「全て」と書いた直後に何ですが、これが本来のRawSetsの考え方のハズなのです。
では実際のCoqのRawSetsの記述を見てみるとどうでしょう。
ほとんどのものはOkという述語を満たすように集合が扱われています。
しかし、「全て」ではありません。


サボったな貴様

これは予想なのですが、おそらくMSetsの実装者は、いくつかの集合の実装をした際に、「この性質は条件付けなくても簡単に成り立つからいいや」と抜かしたのです。
(実装中に必要になったものだけ追加したのでしょう。)
なぜそのような予想を言うかというと、抜けているものがWRawSetsのfilterやpartition、elements、chooseなど、割と実装からならストレートに示せそうな性質ばかりだからです。
(RawSetsのmin_elt_spec1から3は2だけある割にどのくらいの影響があるかすぐには説明できませんが)

なぜ全部に付けない。


こんなことを調べた理由ですが、MSetsでは毎回要素との等価判定をする(または大小関係の比較をする)ので、等価判定が遅い場合、あまり全体のパフォーマンスが上がらないことになります。
(もっとも、WSetsはともかくSetsでは大小関係を付けて大体が木探索を行うので要素数の対数のオーダーでしか比較を行わず、あまり影響がない気もします)
そこで、等価判定や比較を行う際に「代表的な値」を取ってそれ同士で比較できれば、その「代表的な値」同士で、より高速に比較できるのではないか、と考えたのです。
例えば、剰余が等しいかで同値関係を定義した場合、剰余の結果を取ればそれが「代表的な値」になるでしょう。
集合にある要素を全て剰余にしてしまえば、集合側の要素は毎回剰余を計算しなくてよくなりますから、まあ速くなるのではないでしょうか。


これを実装するには、既存の実装を用いて「代表的な値」による集合を構築して、適切にその集合とのやりとりを定義すればよいでしょう。
というわけで実装しようとしたのですが、上の問題で証明できなくなりました。
集合に入っている要素は全て「代表的な値」であるはずなのですが、簡単にそうでないものが入ります。
filterを始める際に、「代表的な値」以外が入っていると(そして条件がなければ簡単に入り得ます)、filter後の条件を容易には示せません。
結果的に、このままではそんな実装はできないわけです。
どう考えてもできるはずなのに!!

ちなみに、もう一つ問題があり、MSets中ではfilterやpartitionはある性質を持ったフィルター用関数についてしか言及しません。
その結果、目標とするものではfilter後の集合が条件を満たすこと(filter_ok)を示せなくなります。
なぜなら、この性質ではfilterに渡す関数に何の仮定も置かれていないからです。
裏側で動いている「代表的な値」の集合は、filterがある特定の性質を満たす場合に限りうまく動きます。
それじゃ渡しても何も言えません。
インターフェースに何もないのですから。


最初、この二つの点を直したMSetsでPull Requestを送ろうと思ったのですが、いかんせん古いライブラリ(FSets)との互換性を保つための部分に問題が起こり、もう知るかと投げ出しました。
(前者は問題ないのですが、後者がインターフェースに性質を追加するので互換性がなくなります)
現状、ほとんどMSetsと同じそれをXSetsという名前で公開しています。
Opam使ったライブラリに登録をしたいところですが、あまりにMSetsと同じコードなのでなんとも気が引ける部分ではあります。
なお、XSetsの使い方はほぼMSetsと同じです。表面的にはインターフェース上に性質が三つ増えているだけなので。

上の問題、MSetsを普通に使っている分には何の問題もないのですが、どうも嫌なところをついてしまったようです。
上で述べた「代表的な値」による実装(XSetRepresentative)を作っては見たのですが、なんだかんだと実装に時間がかかった割に多分あまり速度上がりそうもありません。(実はまだ試してません)
本当はそれを作ってドヤ顔したかったのですが。


最後に、もう一度言いますが、MSetsは普通に使う分には問題なく便利なので、ご心配なく。
posted by chiguri at 00:00| Comment(0) | Coq

2016年07月19日

Android Studioを共有環境に入れよう!

ここでいう共有環境とは、以下のような特徴を持つ七面倒くさい環境を言う。
  • ユーザ個人の情報は Z ドライブにネットワークマウント( SMB で)される。数 GBくらいまで入る。
  • マシンのユーザプロファイル領域は高々数十 MB まででリミットがかかる。
  • ネットワークがプロキシ経由のみ。
  • 誰が何するか分かったものじゃない。
  • 通常利用時は、再起動すると C ドライブがロールバックする。もちろんインストール作業をする場合などはその機能を切れる。
  • ロールバックするので、ユーザプロファイルも消える。

ここまで書くとどこのことか分かるかもしれないが、まあ分かったところでさほど問題がないので気にしないことにする。
誰かが同じような環境にいないとも限らないわけで。


普通に使うと割と便利な Android Studio も、こんな環境では割と地獄になる。


普通に使おうとすると発生する問題


問題は致命的なものがいくつか発生する。一つずつ見ていこう。
  1. Android Studio の設定ファイルが C ドライブのユーザプロファイル以下に作られる(環境変数の有無にかかわらず)。何かのキャッシュが入っているためかそこそこ大きく、プロファイル領域を圧迫(というか平然とキャパオーバー)し、さらに再起動で消える。
  2. 設定しないとインストール時にユーザプロファイル以下に Android SDK を置こうとする。数百 MB である。以下同文。
  3. 起動後に始まるダイアログが、インストール時にどこに置いたかなど全く見ないで、ユーザプロファイル以下に Android SDK があるかを調べ、なかったらこちらが指定するか、さもなくばインストールするぞといってくる。入れたら以下略。しかも、ネットワークにつながっていると最新のファイルをダウンロードしてきて更新しようとする。
  4. ビルドに使う Gradle がユーザプロファイル以下に .gradle というフォルダを作り、そこに設定やらキャッシュやらを貯め込む。他に比べれば大きくないが、ほとんど何もしないプロジェクトでも20MB程度使い、残念ながらこちらの環境ではキャパオーバーする。
  5. プロキシの設定が自動でされないため、ビルド時に Gradle が依存するライブラリを取りに行く際に止まる。拒否されるのではなくタイムアウト待ちになるので、ビルドがちっとも終わらない。当然、長く待った挙げ句にエラーになる。さらに、ネットワークにつながっていると環境をアップデートしようとする。再起動すると戻るのに。
  6. プロジェクトを作るデフォルトの位置が C ドライブのユーザプロファイル以下に AndroidStudioProjects というフォルダになっている。間違ってここに作ると再起動時に消える。そもそもファイルサイズが以下略。

もうインストールやめてもいいだろうか・・・?
いやいや、問題を片付ければ使えるかもしれないじゃないか。続けよう。


問題回避


では一つずつ解消しよう。

  1. Android Studio の設定ファイルの位置は Android Studio をインストールしたフォルダの bin の下にある idea.properties を編集すれば良いようだ。あまり直接はいじらないらしいが、全体としてそうしたいので何も思わない。個々で設定は変えたいので、 Z ドライブ以下に作るようにした。
  2. インストール時には Android SDK を C ドライブの皆が見える位置にインストールする。インストール時に指定するだけなので簡単。
  3. 起動時の Android SDK に関するダイアログについてはスキップするようにした。 idea.properties に disable.android.first.run=true と入れるとスキップできる。どこに書いてあるんだそんなこと。
  4. GRADLE_USER_HOME という環境変数に位置を指定すれば Gradle の設定の位置を変えられるらしいので環境変数を追加した。
  5. Gradleの設定に Offline work というものがあった。これを指定すればネットワーク待ちは回避できる。指定させるのは面倒なのだが、現状設定方法が分からない。
  6. プロジェクトの位置は一度作れば設定ファイルにその場所を格納するので、一度目に口酸っぱく Z ドライブに作るよう言うしかない。これもデフォルト指定ができれば良いのだが、方法が分からない。

さて解決したか・・・と思いきや、まだ問題が発生する。


問題回避による問題


回避策によって起こった問題は以下のもの。
  1. 上の 3 のダイアログスキップの影響で、最初に Android SDK の位置を別途指定する必要が出てきた。
  2. 上の 5 の Offline mode の影響で、依存性解決が出来ない。プロジェクトが最初に作るユニットテスト用の junit も見つからない。
  3. 上の 6 のプロジェクトがネットワークドライブ( SMB )上にあることで、ビルドが極端に遅い。ローカル HDD と比較して 60 倍くらい(秒→分で良い感じ)。どうもファイル一覧の取得が必要になって死ぬほど遅くなってるらしい。

そろそろめげてきただろうか。
しかし回避しなければならない。
  1. 仕方ないので Android SDK のパスを指定させることに。幸い SDK の位置なしにはプロジェクトを作れないので、そうそう問題は起こるまい。
  2. 依存性解決のために、 jar を配置したフォルダを指定する Gradle のスクリプトを配置することにした。 Android Studio をインストールしたフォルダの gradle\gradle-2.10\init.d というフォルダに置かれたスクリプトが必ず実行されるらしいので、そこに allprojects { repositories { flatDir { dirs 'ほげほげ' } } } という記述を入れたファイルを置いた。
  3. 遅いのはビルドの時だけだったので、ビルドディレクトリの指定をした。先ほどの gradle のフォルダに置いたファイルに gradle.projectsLoaded { rootProject.allprojects { buildDir = "C:/Users/${System.env.USERNAME}/AppData/Local/Temp/${rootProject.name}/${project.name}" } } と指定。幸いテンポラリフォルダはプロファイル領域から除外されていたので動く。

次にいこう。まだ終わらない。
  1. 上の 2 の jar の無理矢理な指定のせいで、ユニットテストが動かない。本来はプロジェクトフォルダの app\libs に入れるはずなので、ここに必要なものを持ってこないといけない。
  2. 上の 3 のビルドディレクトリ指定により、再起動時に消えることになる。それを試すと、起動時にあらゆるシンボルの解決に失敗する。なお、ビルドはできる。
  3. ビルドディレクトリの問題か、 Android Studio がある生成物である jar ファイルのロックをかけたまま離してくれない。(フルパスを途中からだけ書くと、 appcompat-v7\23.3.0\jars\classes.jar という名前)

これは解決できるのかって?結果を見てみよう。そろそろ年貢の納め時らしい。
  1. 残念ながら現状回避策なし。試しに必要とおぼしき jar ファイルをいくつか libs の中に入れてみたが、なにやらエラーが出た。もう体力がないので今回は断念。つまりユニットテストをするな、と。(投げやり)
  2. ビルドメニューから Make Project をして、 Fileメニューにある "Invalidate Caches / Restart..." から、 Invalidate and Restart を選ぶ。 Android Studio が再起動するが、環境に問題がなければ、再起動後に indexing が行われてシンボルが解決できるようになる。
  3. 未だに回避策不明。 instant go のせいという Stackoverflowの回答もあったが、どうやらそれではない様子。ロックを強制的に解除するプログラムを使う手も説明されていた。ちょっと怖い。(これ実行に管理者権限いらない?)

というわけで、問題が残ってしまった。


現状


割とどうでも良い制限は以下。
  1. 初回に Android SDK の位置を指定する必要がある。
  2. Gradle の Offline mode を指定する必要がある。
  3. 初回に C ドライブにプロジェクトを作らないようにしないといけない。

そして致命的な問題が以下のもの。
  1. Gradle が jar を消せずに失敗する。回避策不明。 Gradle のメニューから別途ビルドすれば出来なくはないが・・・
  2. ユニットテストができない。もう勘弁してほしい。

現状は以上の通り。解決すれば書き換えたりするかも。続きを読む
posted by chiguri at 20:00| Comment(0) | PC

2016年05月23日

WerckerのBoxの使い方(変更に巻き込まれた話2)

前回はカスタムしたBoxの作り方がずいぶん変わってしまったという話をしたのだが、実は使い方も変わっていたという話。
経緯はほぼ同じ。新しく作ったらBoxの設定ができなかった。

あまりにわからなかったので、サポートにメッセージ投げたら
「Wercker classicのサポートそのうちやめるよ。できれば早く移行してね。一応今は、やりたかったらstep作ってからprivateにし直したらできるよ。」

またstep作るんかい!!

step作るときは、対象のリポジトリがpublic repositoryしかダメなので注意。
リポジトリがprivateだったら、こっそりpublicにして、werckerのstep作って、deploy target消して、stepの設定でprivacyのところをprivateにして、リポジトリへのアクセス方法をdeploy keyに変更して、リポジトリの設定をprivateに直して、さあtrigger。またはgit push。
リポジトリがpublicだったら、werckerのstep作って、deploy target消して、stepの設定でprivacyのところをprivateにして、trigger。

いい加減classicから離れる方法を調べないとなあ。
posted by chiguri at 22:32| Comment(0) | 雑多