[Android] dumpsys storagedの見方

# [dumpsys](https://developer.android.com/studio/command-line/dumpsys)とは

Androidのsystem serviceの状態を出力するコマンド。
オプション無しだと全サービスの情報を出力してくれる。
`dumpsys -l`で対応するserviceの一覧を取得し、`dumpsys SERVICE`で特定のserviceの情報だけ出力することも可能。

dumpsys自体はserviceに対してdump要求を投げるだけで、実際の情報出力はservice側でおこなう。
この作りのおかげで柔軟にdumpsysを拡張できる。
逆に言えばdumpsysに対応するserviceはシステムごとに異なる。

以前["cpuinfo"](https://qiita.com/takeoverjp/items/25b5ceb35a27c0baea1d)を取り上げたので、今回はストレージデバイス使用量の情報を取得する"storaged"の中身を見る。


ソースコードおよび動作確認の環境は以下のとおり。
- Android: [android-8.1.0_r1.0](https://search.siprop.org/android-8.1.0_r1.0/), Oreo,  API 27
- Linux kernel: [android-4.4-o-release](https://android.googlesource.com/kernel/common/+/refs/heads/android-4.4-o-release)

# dumpsys storaged

`dumpsys storaged`は、UIDごとのストレージデバイス使用量統計を出力する。
定期的(デフォルト一時間ごと)に`/proc/uid_io/stats`を収集し、
充電中かどうかに応じて分類したうえで変化量を出力する。

shellからの実行であれば、root権限は不要。
（直接serviceを使う場合は、"android.permission.DUMP"が必要。）

まずは出力例。
起動直後など十分にデータが収集されていない場合は何も出力されないかもしれない。
その場合は、後述の`--force`オプションを指定するとよい。


```shell-session
$ adb shell dumpsys storaged
1567214981,1567215005                                 <------- タイムスタンプ(開始,終了)
shared:android.uid.shell 1105920 0 0 0 0 0 0 0        <------- UIDごとのディスク使用量
1047 405504 0 0 0 0 0 0 0
0 98934784 8777728 0 0 0 0 0 0
1041 471040 0 0 0 0 0 0 0
shared:android.uid.phone 1929216 0 0 0 0 0 0 0
1046 118784 0 0 0 0 0 0 0
1013 847872 0 0 0 0 0 0 0
1036 4096 0 0 0 0 0 0 0
1040 1069056 0 0 0 0 0 0 0
shared:android.uid.system 634400768 2273280 0 0 0 0 0 0
,1567215093                                           <------- タイムスタンプ(,終了)
com.google.android.sdksetup 163840 0 0 0 0 0 0 0
shared:android.media 0 0 1449984 10014720 0 0 0 0
0 6856192 12464128 0 0 0 0 0 0
shared:com.google.android.apps.maps 0 0 31870976 323584 0 0 0 0
com.google.android.apps.nexuslauncher 8339456 6082560 3010560 4096 0 0 0 0
com.android.contacts 0 0 2224128 0 0 0 0 0
```

いつからいつまでの使用量なのかを示すタイムスタンプの後に、
各UIDごとのストレージデバイス使用量が出力される。

## タイムスタンプ行

```
1567214981,1567215005                                 <------- タイムスタンプ(開始,終了)
,1567215093                                           <------- タイムスタンプ(,終了)
```

終了時刻しか書いていない場合、開始時刻は一つ前の終了時刻となる。

タイムスタンプの内容はUNIXタイム。つまり単位は秒。
dateコマンドを使えば、従来の時間に変更可能。

```
$ date -d @1567214981       
Sat Aug 31 01:29:41 GMT 2019
```

## ストレージデバイス使用量行

```
shared:android.uid.shell 1105920 0 0 0 0 0 0 0
1047 405504 0 0 0 0 0 0 0
```

各項目の意味は以下の通り。

1. UID or UIDに対応する名前
2. 非充電 かつ FOREGROUND の読み出し [Byte]
3. 非充電 かつ FOREGROUND の書き込み [Byte]
4. 非充電 かつ BACKGROUND の読み出し [Byte]
5. 非充電 かつ BACKGROUND の書き込み [Byte]
6. 充電中 かつ FOREGROUND の読み出し [Byte]
7. 充電中 かつ FOREGROUND の書き込み [Byte]
8. 充電中 かつ BACKGROUND の読み出し [Byte]
9. 充電中 かつ BACKGROUND の書き込み [Byte]

## オプション

- `--hours <HOURS>`
    - HOURSはdouble型
    - 直近のHOURS時間以降の統計情報のみ出力する。
- `--time_window <TIME_WINDOW>`
    - TIME_WINDOWはint型
    - `/proc/uid_io/stats`を収集する時間間隔を設定する（単位は秒）
    - ただし、`DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT (300)`以上でなければならない。
    - 初期値は、`ro.storaged.uid_io.interval`。
    - `ro.storaged.uid_io.interval`が設定されていない場合は`DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )`。つまり１時間。
- `--threshold <THRESHOLD>`
    - THRESHOLDはuint型
    - IOの総量がTHRESHOLD byte以上のUIDのみ出力するようにする。
- `--force`
    - 強制的に現時点までのストレージデバイス使用量を出力する。

ドキュメントもヘルプも見つからなかった。。。


## 実装

`storaged`自体がストレージデバイス使用量を収集するためのサービスということもあり、
実装はだいぶシンプルで読みやすい。
[system/core/storaged/storaged_service.cppのStoraged::dump()](https://search.siprop.org/android-8.1.0_r1.0/xref/system/core/storaged/storaged_service.cpp)が`dumpsys storaged`に対応する。
前述のオプションなども、ここを見ればわかる。


```cpp:system/core/storaged/storaged_service.cpp
status_t Storaged::dump(int fd, const Vector<String16>& args) {

...

    const std::map<uint64_t, struct uid_records>& records =
                storaged->get_uid_records(hours, threshold, force_report);
    for (const auto& it : records) {
        if (last_ts != it.second.start_ts) {
            dprintf(fd, "%llu", (unsigned long long)it.second.start_ts);
        }
        dprintf(fd, ",%llu\n", (unsigned long long)it.first);
        last_ts = it.first;

        for (const auto& record : it.second.entries) {
            dprintf(fd, "%s %ju %ju %ju %ju %ju %ju %ju %ju\n",
                record.name.c_str(),
                record.ios.bytes[READ][FOREGROUND][CHARGER_OFF],
                record.ios.bytes[WRITE][FOREGROUND][CHARGER_OFF],
                record.ios.bytes[READ][BACKGROUND][CHARGER_OFF],
                record.ios.bytes[WRITE][BACKGROUND][CHARGER_OFF],
                record.ios.bytes[READ][FOREGROUND][CHARGER_ON],
                record.ios.bytes[WRITE][FOREGROUND][CHARGER_ON],
                record.ios.bytes[READ][BACKGROUND][CHARGER_ON],
                record.ios.bytes[WRITE][BACKGROUND][CHARGER_ON]);
        }
    }

...

    return NO_ERROR;
}
```

## `/proc/uid_io/stats`

`dumpsys storaged`の基となる`procfs`。
Androidで独自に追加されている。
出力はぱっと見`dumpsys storaged`と似ているが、中身は全然違うので要注意。
さらに、[公式ページ](https://source.android.com/devices/tech/debug/storaged)の説明も間違っている(2019/8/31時点)ので注意。
参照にはroot権限が必要。

```shell-session
$ cat /proc/uid_io/stats                                                                                                                                                                         
0 69488496 57508693 176613888 71938048 0 0 0 0 37 0
10074 0 0 0 0 478411 2454202 8970240 3514368 0 259
1021 523337 3579 2101248 0 0 0 0 0 0 0
10019 13994 828 163840 0 10502 1521 0 0 0 0
...
```

1. UID
2. FOREGROUND かつ read system call [Byte]
3. FOREGROUND かつ write system call [Byte]
4. FOREGROUND かつ storage deviceからの読み出し [Byte]
5. FOREGROUND かつ storage deviceへの書き込み [Byte]
6. BACKGROUND かつ read system call [Byte]
7. BACKGROUND かつ write system call [Byte]
8. BACKGROUND かつ storage deviceからの読み出し [Byte]
9. BACKGROUND かつ storage deviceへの書き込み [Byte]
10. FOREGROUND かつ fsync system call [回数]
11. BACKGROUND かつ fsync system call [回数]


実装は以下。

```c:drivers/misc/uid_sys_stats.c
static int uid_io_show(struct seq_file *m, void *v)
{
        struct uid_entry *uid_entry;
        unsigned long bkt;

        rt_mutex_lock(&uid_lock);

        update_io_stats_all_locked();

        hash_for_each(hash_table, bkt, uid_entry, hash) {
                seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
                                uid_entry->uid,
                                uid_entry->io[UID_STATE_FOREGROUND].rchar,
                                uid_entry->io[UID_STATE_FOREGROUND].wchar,
                                uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
                                uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
                                uid_entry->io[UID_STATE_BACKGROUND].rchar,
                                uid_entry->io[UID_STATE_BACKGROUND].wchar,
                                uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
                                uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
                                uid_entry->io[UID_STATE_FOREGROUND].fsync,
                                uid_entry->io[UID_STATE_BACKGROUND].fsync);

                show_io_uid_tasks(m, uid_entry);
        }

        rt_mutex_unlock(&uid_lock);
        return 0;
}

```

# おまけ：UIDごとの累積使用量が知りたいときは`storaged -u`

`/pro/uid_io/stats`だとUIDで出力されるので、わかりづらい。
`dumpsys storaged`はUIDがpackage名なのでわかりやすいが、累積使用量を知るためには処理が必要。

という場合は`storaged -u`を使えばいい。
デーモンプロセスを実行するのは抵抗があるかも知れないが、
UIDをpackage名に変換して、`/proc/uid_io/stats`相当の情報を出力してくれる。
ヘッダをつけてくれるのも地味にありがたい。

```
# storaged -u
name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync
shared:com.google.uid.shared 333142 4077 159744 16384 119379524 25551230 139603968 67715072 0 2766
com.google.android.googlequicksearchbox 1552289 1046016 1654784 237568 2588581 6417348 65368064 6144000 3 223
shared:com.google.android.apps.maps 0 0 0 0 917109 59377 62382080 90112 0 15
com.android.chrome 1944 213 0 0 1645705 1542720 38596608 18350080 0 61
com.google.android.inputmethod.latin 1944 213 0 0 1740052 1965819 43499520 524288 0 12
com.google.android.dialer 1944 203 0 0 17437687 30775961 13991936 25989120 0 47
com.google.android.apps.photos 0 0 0 0 2109896 849853 31760384 880640 0 4
```


# 参考
- [dumpsys](https://developer.android.com/studio/command-line/dumpsys)
- [storaged](https://source.android.com/devices/tech/debug/storaged)
    - 注：`/proc/uid_io/stats`のフォーマットに関する記述は誤り(2019/8/31時点)
- [Androidソースコード検索サービス](https://sites.google.com/site/devcollaboration/codesearch)
- [android-8.1.0_r1.0](https://search.siprop.org/android-8.1.0_r1.0/)
- [Linux kernel android-4.4-o-release](https://android.googlesource.com/kernel/common/+/refs/heads/android-4.4-o-release)