KVM で VM を実行している際にホストの /proc/stat が返す user 時間についての注意点

KVMVM を動かしている際に、ホストが返す /proc/stat 数値の扱いにはちょっとした注意が必要

/proc/stat の man

/proc/stat について説明した man 5 proc には下記の通りの説明がある

user (1) ユーザーモードで消費した時間。
guest (Linux 2.6.24 以降) (9) Linux カーネルの制御下のゲストオペレーティングシステムの仮想 CPU の 実行に消費された時間。

実はこの user には guest 分が重複して加算されている。そんでもって、この仕様は man には記載されていない。(英語の man でも同様)

ソースで説明

KVMVM を動かしている際に消費した CPU時間を計測しているのは account_guest_time である。 (この記事を書いている時点で) 最新の 4.13-rc4 では下記の通りのソースだ

/*
 * Account guest cpu time to a process.
 * @p: the process that the cpu time gets accounted to
 * @cputime: the cpu time spent in virtual machine since the last update
 */
void account_guest_time(struct task_struct *p, u64 cputime)
{
    u64 *cpustat = kcpustat_this_cpu->cpustat;

    /* Add guest time to process. */
    p->utime += cputime;
    account_group_user_time(p, cputime);
    p->gtime += cputime;

    /* Add guest time to cpustat. */
    if (task_nice(p) > 0) {
        cpustat[CPUTIME_NICE] += cputime;
        cpustat[CPUTIME_GUEST_NICE] += cputime;
    } else {
        cpustat[CPUTIME_USER] += cputime; ⭐
        cpustat[CPUTIME_GUEST] += cputime; ⭐
    }
}
  • cpustat[CPUTIME_USER] が user 時間
  • cpustat[CPUTIME_GUEST] が guest 時間

両者に cputime を加算しているのがポイント

この計測方法は v2.6.24-rc1 から取り入れられている。その時の実装を貼る

/*
 * Account guest cpu time to a process.
 * @p: the process that the cpu time gets accounted to
 * @cputime: the cpu time spent in virtual machine since the last update
 */
void account_guest_time(struct task_struct *p, cputime_t cputime)
{
    cputime64_t tmp;
    struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat;

    tmp = cputime_to_cputime64(cputime);

    p->utime = cputime_add(p->utime, cputime);
    p->gtime = cputime_add(p->gtime, cputime);

    cpustat->user = cputime64_add(cpustat->user, tmp); ⭐
    cpustat->guest = cputime64_add(cpustat->guest, tmp); ⭐
}

現在のと比較してリファクタリングされているのがわかるが、意味するところは変わらない

ユーザランドツールの実装をどうすべきか

mpstat を参考にする。先に述べた点を考慮して mpstat は user から guest を減算して補正している

https://github.com/sysstat/sysstat/blob/master/mpstat.c#L529-L568 (d7321c1)

/*
 ***************************************************************************
 * Display CPU statistics in JSON format.
 *
 * IN:
 * @tab        Number of tabs to print.
 * @g_itv  Interval value in jiffies multiplied by the number of CPU.
 * @prev   Position in array where statistics used as reference are.
 *     Stats used as reference may be the previous ones read, or
 *     the very first ones when calculating the average.
 * @curr   Position in array where current statistics will be saved.
 ***************************************************************************
 */
void write_json_cpu_stats(int tab, unsigned long long g_itv, int prev, int curr)
{
    struct stats_cpu *scc, *scp;
    unsigned long long pc_itv;
    int cpu, next = FALSE;

    xprintf(tab++, "\"cpu-load\": [");

    /* Check if we want global stats among all proc */
    if (*cpu_bitmap & 1) {

        next = TRUE;
        xprintf0(tab, "{\"cpu\": \"all\", \"usr\": %.2f, \"nice\": %.2f, \"sys\": %.2f, "
                  "\"iowait\": %.2f, \"irq\": %.2f, \"soft\": %.2f, \"steal\": %.2f, "
                  "\"guest\": %.2f, \"gnice\": %.2f, \"idle\": %.2f}",
             (st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest) < ⭐
             (st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest) ? ⭐
             0.0 :
             ll_sp_value(st_cpu[prev]->cpu_user - st_cpu[prev]->cpu_guest, ⭐
                     st_cpu[curr]->cpu_user - st_cpu[curr]->cpu_guest, ⭐
                     g_itv),

感想

  • ソースを読んだら意外な事実が発覚することが Linux カーネルでは時折ある
  • ところで KVMVM を動かしてないなら /proc/stat の guest は増えないはずなので別に支障はない
  • /proc/stat を読み取っても guest を捨てて計算しておいたら支障はない
    • sar は %guest を表示しない。どうやって計算しているんだろ?
root@test001:~# sar 1
Linux 4.4.0-66-generic (001)    10/11/17    _x86_64_    (48 CPU)

12:56:35        CPU     %user     %nice   %system   %iowait    %steal     %idle
12:56:36        all     18.37      0.00      4.31      0.19      0.00     77.13
12:56:37        all     29.69      0.00      5.02      0.32      0.00     64.98
12:56:38        all     24.02      0.00      4.08      0.38      0.00     71.52
12:56:39        all     31.86      0.00      3.62      0.29      0.00     64.22

エントリを書いたモチベーション

いつもお世話になっている GitHub の mackrelio/mackerel-agent にほぼ同様の内容で issue 報告していたのでした

github.com

mackerel に閉じた話題ではないので、 issue とは別の場所でもまとめておいたほうがいいだろうと思い本エントリを記した

目白台、松郷峠 🚲

9/30

目白台をうろうろ

学生の時によくいってた目白台のお弁当屋さんで昼飯を買う

明治通り目白台神田川が土地をえぐったせいか 急峻な坂が多い (目白崖線)。明治通り近くの のぞき坂 は勾配が 22% (25%?) もあって関東の山中でも滅多にみないような激坂。負荷をかけてのぼるにはちょっと短いので、ゆっくりトルクをかけてフォームを整える練習によさそう

そうそう、都内の急な坂を集めた面白ブログがあって ここにも書かれてる

d.hatena.ne.jp

(追記) あんちぽくんも同じ場所の写真を載せてるね

http://blog.kentarok.org/entry/2017/07/08/000000blog.kentarok.org

ブエルタ・ア・エスパーニャ はこんな斜度の山々を上りまくるのだから激しいなぁ

10/1

ときがわまで北上して、小柄な松郷峠をのぼっておしまい

松郷峠は ときがわベース の T字路を北に向かったところに位置する。白石峠に比べたら小柄なので楽ちん。眺めがよいとか そういう峠じゃないので「行ったよ」という記録を残すためだけだな。近所にあったらトレーニングに格好の場所だなー

写真を撮るモチベが薄かったので これでおしまい

Out of memory and no killable processes... のカーネルパニックを起こしたい (2) - netfilter のフックを利用する編

hiboma.hatenadiary.jp

前回の続き

割り込みコンテキスト

前回はバグったカーネルモジュールを書いて、カーネルモジュールの初期化フェーズでバグったコードを実行してカーネルパニックを起こした。

プロセスコンテキストでバグを起こすのに なんかしっくりこない感じがあるので、{ 割り込み, top half, bottom half } の中で異常を起こしてみたい

netfilter のフック

ネットワーク越しに何かパケットやデータグラムを受け取った際に、カーネル内で任意のコードを実行できないかを考えてみた。iptables の拡張を書く方法を調べたりもしたが、iptables パッケージのビルドとカーネルモジュールのビルドが必要で少し面倒。

1枚のカーネルモジュールだけで作るには下記のエントリを参考にしたところ楽に実装できた

blog.mono0x.net

netfilter のフックを利用して「パケットがきたら foo bar」するといったファイヤウォールのような機能がつくれるらしい

続きを読む

Out of memory and no killable processes... のカーネルパニックを起こしたい

イントロ

dsas.blog.klab.org

こちらのエントリを拝見したのだが Out of memory and no killable processes... というログを出してカーネルパニックするケースがあるのだなと初めて知った。

幸いにして、過去に同様のメッセージを出すカーネルパニックには遭遇したことがない。あるいはログが取れてなくて気がつかなかっただけかもね!

モチベーション

故意に異常を起こして、このカーネルパニックを見てみたい 🔥💀

続きを読む

榎峠、小沢峠、天目指峠 🚲

9/24(日) 青梅街道から成木・名栗へ。ふだんはスルーして通過している峠をのぼってきた

走行距離 127km、 獲得標高 1171m。

小一時間かけてヘビーな峠にチャレンジするのも大きな達成感を味わえるけど、小ぶりな峠をいくつか組み合わせてトライするのも楽しい 😊

続きを読む

GDB で php プロセスの覗き見

phpGDBデバッグするのが便利。 以下のエントリで手法が説明されている

qiita.com

blog.anatoo.jp

stackoverflow.com

以降、私が書くのはこれらのエントリに書かれている手順を真似たものなので、詳細を知るにあたっては是非リンク先も読んでほしい

検証: GDB で実行中の php プロセスを覗き見る

GDB は実行中のプロセスを解析の対象とすることもできる。このエントリでは、無限ループするバグった phpGDB で覗いてみよう

続きを読む