2013年2月20日水曜日

Inside KyoroStress -1- Create Low Memory Killer situation on purpose!!

 Android PF have kill alive process when heigher priority process need heap. 
 This report  explain that produce Low Memory Killer situation on purpose

 You think it so simple. just to make application to consume many heap.
 but, it difficult that your thinking way.
 following problem
  - A. Android PF restrict application java heap per one process.
  - B. heap comsuming application is killed by Android PF.
  
 This report introduce KyoroStress 's solution.

[KyoroStress solution]
  Kyoro Stress done following approach.
    - 1. Booting many service in the process of differing respectively.
    - 2. Each Service consumes a lot of heaps. 

  The problem of A is solved by booting many process, and B too,
  alive heap is more consume instead of kill service's heap 


[BigEater(consuming heap service) 's senario]
  - 1. consume the specified heap.
     if retry is true, then consuming heap is repeated repeatedly until the specified heap is consumed
  - 2. recovery killed service
     if retry is true, then repeated repeatedly until killing this working thread.
  - 3. END

 you think. if ALL Service is killed by PF, when this senario is failed.
 but no problem. PF revcovery Killed service.
 so, keeping to consuming heap situation.

[KyoroStressV2 's manual]

  # start 
   start consuming heap.
  # stop
   stop consuming heap. and release.
  # num of big eater
   booting process num.
  # eatup java heap size
   consuming heap size per one process.
  # is retry
   retry option
  # show notification
   if true, consume notification.
 # lowMoemory
   if true, mean low memory state
  # availMemory
   available memory,
  # threshold
   boundary low memory state.
  # dalvik.vm.heapsize 
   if android:largeHeap="true" 's application available heap size.
  # dalvik.vm.heapgrowthlimit
   if android:largeHeap="false" 's application available heap size.
  # lahalito
   now consuming java heap
  # kadorto
   now recovery another killed process.
  # done all task
  all task is end.

[Output]
  There is apk and source is following site 
   https://github.com/kyorohiro/KyoroStressV2
  Google Play address is following 
   https://play.google.com/store/apps/details?id=info.kyorohiro.helloworld.stressv2

2013年2月17日日曜日

KyoroStressの技術 -1- Low Memory Killer を意図的に発生させたい

[課題] Low Memory Killer を意図的に発生させたい
  Androidには、ヒープが涸渇すると使われていないアプリをKillする機能があります。
  この記事では、意図的にヒープを枯渇させて、この状態をつくる方法について説明します。
  
  単純にヒープを大量に消費するアプリを作成すれば良いように思えます。
  しかし、これだけでは上手くいきません。
    -A ひとつのアプリで消費できるヒープが制限されているため、ひとつのアプリで端末のヒープが涸渇している状態をつくれない。
    -B ヒープを涸渇しているアプリがPFにKILLされる場合がある。
   といった問題があります。

  KyoroStressV2での解決方法を紹介します。


[KyoroStressでの解決方法]
  Kyoro Stress では、以下のような方法をとりました。
    - 1. 複数のServiceを、各々異なるプロセスで起動する。
    - 2. 各々Serviceで大量のヒープを消費する。

  複数のプロセスを立ち上げれば、PFのヒープを枯渇させることができます。これで、(A)の問題が解決できました。
  また、Bについては、「生きているプロセス」が「KILLされたプロセス」の分もヒープを消費すれば上手くいけそうです。

[BigEater(ヒープ消費サービス)の動作]
  KyoroStressV2で、ヒープを消費するサービスは以下のシナリオで動作しています。
  - 1. 指定されたヒープを取得する。
     is retry が true の時、指定されたヒープを取得できるまで、1を何度も繰り返す。
  - 2. KILLされたサービスを復活させる。
       is retry が true の時、Threadが死ぬまで、何度も2を繰り返す。
  - 3. 終了
  といった感じです。
  このままでは、すべてのServiceがPFにKILLされたら上手くいかないように思うかも知れません。
  しかし、時間がたつと(数秒)、PFはKILLしたServiceを再起動します。
  このため、ServiceがすべてKILLされても、ヒープを大量に消費しようとする状態は保持されます。


[使い方]
  KyoroStressV2の操作方法について説明します。
  # start 
      ヒープの消費を開始します。
      ヒープの消費率監視を開始する。
  # stop
      ヒープの消費を終了する。そして、開放する。
  # num of big eater
      起動するプロセスの数
  # eatup java heap size
      ひとつのプロセスが消費するヒープサイズ
  # is retry
      オンならば、ヒープを確保できるまで、何度もトライする。
  # show notification
      オンならば、Notificaiotn表示をする。
 # lowMoemory
      true ならば、 ロウメモリー状態
  # availMemory
   使用可能なメモリ
  # threshold
      この値よりも低い場合は、ロウメモリー状態
  # dalvik.vm.heapsize 
      ひとつのプロセスがandroid:largeHeap="true"の時に使用可能なJavaヒープ
  # dalvik.vm.heapgrowthlimit
      android:largeHeap="false"の時に使用可能なJavaヒープ
  # lahalito
      指定されたヒープを取得しにいっている状態
  # kadorto
      PFのKILLされたサービスを再起動させようとしている状態
  # done all task
    Serviceはすべての作業を完了した。そのため、もう何もすることはない状態


[成果物]
  以下の場所にソースとAPKがあります。
   https://github.com/kyorohiro/KyoroStressV2
  Google Playでも公開しています。
   https://play.google.com/store/apps/details?id=info.kyorohiro.helloworld.stressv2

[次回]
   より、詳細な仕組みは別の記事で解説します。
 - Serviceごとに、別プロセスにする方法は?
 - アプリが使用可能なヒープを調べる方法は?
 - Low Memory 状態を取得する方法は?
 - 制限を越えてヒープを取得する方法?

-------
kyorohiro work

kyorohiro.strikingly.com

2013年2月11日月曜日

KyoroText で使用されている技術 その12 AndroidでCoverageを計測したい


[課題] カバレッジを計測したい!!


基本、アプリ作成する時は、ユニットテスト(JUnitとか)を書きながら、コードを書いていきます。
動作確認しながらコードが書けるので作業がはかどります。

しかし、自分が書いたユニットテストが想定した通りのできか?うまくテストできているか?
を確認するすべはありません。

どの程度テストできているかを定量的に客観的に数値化する方法があれば、
それらを確認するすべになります。※テスト作成のど忘れとした時に、気がつくことができます!!

KyoroTextでしようとしている方法について解説します。


客観的なデータがあれば、うまく想定したとおりにテストできていることが保障できます。
保障できないにしても心の安心を得ることはできるでしょう。


[カバレッジ]
 どの程度テストができているかを計る方法として、カバレッジという表現があります。
 まずは、AndroidでCoverageをとる方法について解説します。

 #カバレッジとは何か?
  Coverageは、テストした時にどの程度の網羅性があるかを数値化します。
  具体的には、「テストできたクラスはのどのくらい?」「テストできたメソッドはどのくらい?」
 「テストできた行はどのくらい?」といった事を計測するのです。

 例えば、以下のようなコードがあったとして
 public void test(int a) {
   if(a>10) {
      --A--
   } else {
      --B--
   }
 }
 aが、11の時だけテストしたとすると、50%のテストができたといえます。
 aが、11の時と、aが10の時をテストした場合、100%のテストができたといえます。

 このように、自分が書いたコードがテストとできているかを、客観的にあらわす事ができるのです。
 これがCoverageです。


[EMMA]
 EMMAというツールを使うことで簡単にカバレッジをとる事ができます。
 EMMAはJavaのクラスファイルを書き換えます。
 すべてのif文の前後、メソッドの先頭等に、Coverageを測定するためのコードを埋め込みます。

 EMMAは単純に、埋め込んだコード上で通過した部分を記録しておき。アプリが終了するタイミングで、そのデータをストレージ上に記録します。


[EMMA Android]
 Androidには、EMMAがすでにSDKに含まれています。
 今回はそのまま、Androidに含まれている機能を使用することにしました。
 ※ 実現方法とかは機会があれば解説したいです。今回はしません。

 1. antからビルドできるようにする。
  ---> cd {ターゲットのprojectがある場所}
  ---> android.bat update project --path .
 2. テストプロジェクトともantからビルドできるようにす。
  ---> cd {ターゲットの test projectがある場所}
  ---> android update test-project -m <ターゲットのprojectの絶対パス> -p .
 3. emma用にAPKをビルドする。
  ---> cd {ターゲットのprojectがある場所}
  ---> ant emma debug install
 4 . テストプロジェクトもビルドする。
  ---> cd {ターゲットの test projectがある場所}
  ---> ant emma debug install test


後は結果がでます。

[成果物]
#サンプルのコードを書きました。
https://github.com/kyorohiro/KyoroSamples/tree/master/EmmaSample
 - runtest.sh
   テスト実施します。
 - createbuildxml.sh
   ビルド環境を作ります。

#以下のような、Coverageの結果が出力されます。
https://github.com/kyorohiro/KyoroSamples/tree/master/EmmaSample/tests/bin

[参考]
http://d.hatena.ne.jp/halts/20120201/p1
http://blog.pboos.ch/post/35269158339/android-coverage-report-for-unit-tests


[次回]
Google Playで、KyoroStressが1000ダウンロードを超えました。KyoroStressについて解説します。
Androidで、アプリが殺されるタイミングは何?殺された時、殺された後のアプリの動作は何?
といった事を解説します。

2013年2月10日日曜日

P2P探訪 Raider その1-2 Torrentファイルフォーマット


というわけで、前回に引き続いて、この記事ではTorrentファイルについて説明します。


[Torrent file format]
前回、Bencodingを実装したのでTorremt Fileを読み込めることができるようになりました。
今回は、Torrentファイルから必要な情報を読み込む方法について解説します。

torretファイルから取得できる情報はどんなものかは、別の機会に解説します。
ここでは、torrentファイルには 2つのフォーマットがあることとデータ構造を説明します。
たとえば、「"announce"というデータが何なのか?」については解説しません。


torrentファイルでは、ダウンロード/アップロードの対象としているファイルが、ひとつの場合と複数の場合で構造がすこしだけことなります。

ひとつの時を、「single file」 複数の時を「multi file」と呼ぶことにます。

では、データ構造を紹介します。

- single file pattern
bendiction 
  benstring "announce"
  beninteger "creation date"
  bendiction "info"
    beninteger "length"
    benstring "name"
    beninteger "piece length"
    bebstring "pieces"

- multi file pattern
bendiction 
  benstring "announce"
  beninteger "creation date"
  bendiction "info"
    benlist "files"
      bendiction
        beninteger "length"
        benlist "path"
           benstring
      bendiction
        ..
        .. 
          ..         
    beninteger "length"
    benstring "name"
    beninteger "piece length"
    bebstring "pieces"

といった感じです。
multi file の時は、filesというDictionが追加されるわけです。


[reference]
- 成果物 torrent file
  https://github.com/kyorohiro/Raider/tree/master/Raider/src/info/kyorohiro/raider/util/torrent
  
- Torrent spec
  http://en.wikipedia.org/wiki/Torrent_file
  http://www.bittorrent.org/beps/bep_0003.html
  http://sourceforge.net/projects/bittorrent/
 

P2P探訪 Raider その1-1 Bencoding

前回の記事を書き直しました。説明不足を補います。

 課題1 Torrent File に記載されている情報を読み込みたい。
BitTorrent のプロトコルを学習するに当たって、実際に、Torrentクライアントを実装することにしました。
まず最初に、TorrentFileに記載されている情報を読み取ることから始めたいと思います。

Torrentファイルは、bencode というルールで、記述されています。
そこで、
1. Bencodingを扱えるようにする。
2. Bittorrentファイルを読み込めるようにする。
の順に問題を解決していくことにしました。

というわけで、この記事ではBencodingについて説明します。

Bencoding
bencodingは integer, string(byte array), list, diction のデータ型を持ちます。
なので、この4つデータ型のデコードとエンコードができるようになれば、
bencodingを扱えるようになったことになります、

具体的に、以下のようなルールでエンコードされいます。
  beninteger   : "i" [0-9]* "e"
  benstring    : [0-9]* ":" <bytes array/string> 
     # bytes array/string length is prev [0-9]*.
  bendiction   : "d" dictelements "e" 
  benlist      : "l" listelements "e"
  benobject    : beninteger | benstring | beniction | benlist
  listelements : benobject (benobject)*
  dictelements : benstring benobject (benstring benobject)*

といった感じです。
例えば、
  Integerで、1を表したい場合は、"i1e"
  Stringでabcを表したい場合は、"3:abc"
  上記を含むListを表したい場合は、"li1e3:abce"
  といった感じで表せます。


また、bencodingは簡単にパースできるように工夫されています。
簡単といえるのは、先頭1文字で、どのデータ構造でできているかが判断できるようになっているからです。

なので、bencodingのdecoderは、以下のようなシナリオで実現できます。
1. 一文字読み込む。
2. どのデータか判別する。
例えば、 'i' ならば、integer、's'ならば、stringといった感じです。
3. データ形式に応じて読み込む。
4. 1に戻る。


RaiderでのParse方法
Bittorrent のbencodingのencode/decode方法はとてもシンプルです。
Bittorrentのbencodingに関する部分のコードをチックすると良いでしょう。ですが、Radierでは LLライクな独自の方法を採用しました。
特に理由はありません。仕様から空で考えたら、別の実装になってしまいました。

パースの仕組みについて、簡単に解説します。 LLでは文法とJava Method が一対一で対応付けることができます。
なので、対応付け方法がわかれば、機械的な作業になります。
※ なので普通は、antlrだとか javaccとか、yaccとか bisonとか ツールを使って実現したりします。


例えば、以下のような文法の場合
   benstring    : "i" [0-9]* "e"
   beninteger   : "i" [0-9]* "e"
   benobject    : beninteger | benstring

以下のような感じで一対一の対応で書けます。
 static void benstring(reader) throws xxException {
    try {
       reader.mark(); 
       if(check(string grammer)) {
       } else {
          reader.backtoMark(); // パースに失敗した時に、ポインターの位置を元に戻す。
          throw xxException;
       }
    } finally {
      reader.releasemark();
    }
 }

 static void beninteger(reader) throws xxException  {
    try {
       reader.mark();
       if(check(integer grammer)) {
       } else {
          reader.backtoMark(); // パースに失敗した時に、ポインターの位置を元に戻す。
          throw xxException;
       }
    } finally {
      reader.releasemark();
    }
 }
 static void benobject(reader) throws xxException {
     try {benstring()}catch(xxException e){}
     try {beninteger()}catch(xxException e){throw e;}
 }



[reference]
- 成果物、bencoding
  https://github.com/kyorohiro/Raider/tree/master/Raider/src/info/kyorohiro/raider/util/bencode

- Torrent spec
  http://en.wikipedia.org/wiki/Torrent_file
  http://www.bittorrent.org/beps/bep_0003.html
  http://sourceforge.net/projects/bittorrent/
  
- LL Parser
  http://pragprog.com/book/tpdsl/language-implementation-patterns
  

P2P探訪 Raider その1 Bittorrent File Format



[Raider Purpose]
 P2Pについて学習するために、Raiderというメタファーのgithubリポジトリを作成しました。
 まずは、もっとも有名な Torrentプロトコルを実装してみようと思います。 
 
 私にとっては、BitTorrentの仕様書だけを頼りに実装するのは難しいことです。
 そこで、Raiderでは、以下のアプローチとっています。
  1. bittorrentについて、実現できそうなシナリオを作る。
  2. 調査しながら、コードを書く
  3. 評価して、次のシナリオを決める。

 今は最初に実現しようとしているのは、以下のようなものです。
  # torrentファイルの読み込みと作成をする。
  これなら、簡単に実現できそうですね!!
 

[Torrent file format]
# bencoding
Torrent file は bencoding によって構成されています。 まずは、bencoingを扱えるようにする必要があります。
bencodingは integer, string(byte array), list, diction で構成されています。
bencoding は以下のような簡単なルールでencodeされます。

  beninteger   : "i" [0-9]* "e"
  benstring    : [0-9]* ":"  # bytes array/string length is prev [0-9]*.
  bendiction   : "d" dictelements "e" 
  benlist      : "l" listelements "e"
  benobject    : beninteger | benstring | beniction | benlist
  listelements : benobject (benobject)*
  dictelements : benstring benobject (benstring benobject)*

簡単といえるのは、先頭1文字で、どのデータ構造でできているかが判断できるようになっているからです。
例えば、 integerならば、'i' stringならば、's'といった感じです。
なので、bencodingのdecoderは、以下のようなシナリオで実現できます。
1. 一文字読み込む。
2. どのデータか判別する。
3. データ形式に応じて読み込む。
4. 1に戻る。

# torrent file format
bencodingが扱えるようになれば、torrent fileから必要な情報を取得することができるようになります。
torret file から取得できる情報はどんなものかは、別の機会に解説します。ここでは、torrent fileには 2つのフォーマットがあることとデータ構造を説明します。

torrent fileでは、ダウンロード/アップロードの対象としているファイルが、ひとつの場合と複数の場合で、構造がすこしだけことなります。参考までにデータ構造を紹介します。

- single file pattern
bendiction 
  benstring "announce"
  beninteger "creation date"
  bendiction "info"
    beninteger "length"
    benstring "name"
    beninteger "piece length"
    bebstring "pieces"

- multi file pattern
bendiction 
  benstring "announce"
  beninteger "creation date"
  bendiction "info"
    benlist "files"
      bendiction
        beninteger "length"
        benlist "path"
           benstring
      bendiction
      ..
      ..          
    beninteger "length"
    benstring "name"
    beninteger "piece length"
    bebstring "pieces"

[Raider's parse system]
Bittorrent のbencodingのencode/decode方法はとてもシンプルです。Bittorrentのパースをチックすると良いでしょう。ですが、Radierではね LLライクな方法を採用しました。特に理由はありません。仕様から空で考えたら、別の実装になってしまいました。


- comment about LL parse 
LL については、 [*3] を参照してください。
簡単にコメントします。 LLは文法とJava Method が一対一で対応付けられます。
※ 機械的な作業で実現できます。

例えば、以下のような文法の場合
   benstring    : "i" [0-9]* "e"
   beninteger   : "i" [0-9]* "e"
   benobject    : beninteger | benstring

以下のような感じで一対一の対応で書けます。
 static void benstring(reader) throws xxException {
    try {
       reader.mark();
       if(check(string grammer)) {
       } else {
          reader.backtoMark(); 
          throw xxException;
       }
    } finally {
      reader.releasemark();
    }
 }

 static void beninteger(reader) throws xxException  {
    try {
       reader.mark();
       if(check(integer grammer)) {
       } else {
          reader.backtoMark(); 
          throw xxException;
       }
    } finally {
      reader.releasemark();
    }
 }
 static void benobject(reader) throws xxException {
     try {benstring()}catch(xxException e){}
     try {beninteger()}catch(xxException e){throw e;}
 }

bencoding
https://github.com/kyorohiro/Raider/tree/master/Raider/src/info/kyorohiro/raider/util/bencode

torrent file
https://github.com/kyorohiro/Raider/tree/master/Raider/src/info/kyorohiro/raider/util/torrent

[reference]
- Torrent spec
  http://en.wikipedia.org/wiki/Torrent_file
  http://www.bittorrent.org/beps/bep_0003.html
  http://sourceforge.net/projects/bittorrent/
  
- LL Parser[*3] 
  http://pragprog.com/book/tpdsl/language-implementation-patterns