Friday, June 12, 2020

AndroidのPerfMon解析 その2

前回の続き。PerfMonを解析していく。

前回はアプリ起動時に実行されるActivityから順に処理を追っていたが、プログラムがデコンパイルしたソースということもあり順に追っていくのは大変そうなのでアプローチを変えてみる。

/proc/以下や /sys/以下のハードウェア情報にアクセスしてると予想できるのでソースファイルの入ったフォルダで文字列検索


$ grep -R "/proc/" ../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/
../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/ac.java:            BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/stat"));
../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/ac.java:                        StringTokenizer stringTokenizer = new StringTokenizer(new BufferedReader(new FileReader(String.format("/proc/%d/stat", new Object[]{Integer.valueOf(iArr[i5])}))).readLine());
../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/ac.java:            BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/dev"));
ac.javaだけヒットした。このファイルを開いてみた。

        for (int i = 0; i < 256; i++) {
            if (new File("/sys/devices/system/cpu/cpu" + String.valueOf(i)).exists() && i + 1 > this.a) {
                this.a = i + 1;
            }
        }
まずはコンストラクタにあるこのコード。/sys/devices/system/cpu/下にあるcpu0,cpu1,cpu2...というフォルダを数えることでコア数を数えている。
Android10でのPerfMon
コア数を数えることはできているようだ。


            BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/stat"));
            for (String readLine = bufferedReader.readLine(); readLine != null; readLine = bufferedReader.readLine()) {
                if (readLine.startsWith("cpu")) {

次にこのコード。/proc/statの中身を読んでそれぞれの数値を抽出してる部分である。

ファイルの中身を読むbuffereReaderをsudo cat /proc/statの出力ストリームに変えればよさそうである。


        while (i <= this.a) {
            try {
                agVarArr[i].j = a(String.format("/sys/devices/system/cpu/%s/cpufreq/scaling_min_freq", new Object[]{agVarArr[i].a}), 0);
                if (agVarArr[i].j == 0) {
                    agVarArr[i].j = a(String.format("/sys/devices/system/cpu/%s/cpufreq/cpuinfo_min_freq", new Object[]{agVarArr[i].a}), 0);
                }
                agVarArr[i].k = a(String.format("/sys/devices/system/cpu/%s/cpufreq/scaling_cur_freq", new Object[]{agVarArr[i].a}), 0);
                if (agVarArr[i].k == 0) {
                    agVarArr[i].k = a(String.format("/sys/devices/system/cpu/%s/cpufreq/cpuinfo_cur_freq", new Object[]{agVarArr[i].a}), 0);
                }
                agVarArr[i].l = a(String.format("/sys/devices/system/cpu/%s/cpufreq/scaling_max_freq", new Object[]{agVarArr[i].a}), 0);
                if (agVarArr[i].l == 0) {
                    agVarArr[i].l = a(String.format("/sys/devices/system/cpu/%s/cpufreq/cpuinfo_max_freq", new Object[]{agVarArr[i].a}), 0);
                }
                agVarArr[0].j = Math.max(agVarArr[0].j, agVarArr[i].j);
                agVarArr[0].k = Math.max(agVarArr[0].k, agVarArr[i].k);
                agVarArr[0].l = Math.max(agVarArr[0].l, agVarArr[i].l);
                i++;
            } catch (Exception e3) {
            }
        }

/sys/devices/system/cpu/cpu[0-9]/cpufreq/は各コアの動作クロックの情報がある。a()という関数にかけてるのでa()を見てみる。


    private int a(String str, int i) {
        try {
            return Integer.parseInt(new BufferedReader(new FileReader(str)).readLine(), 10);
        } catch (Exception e2) {
            return i;
        }
    }

/proc/statを読む部分と同じである。この部分もsuを使えば良さそうである。

Patch

実際にsuを使ったコードを書いてみる

    public static BufferedReader sudoCat(String filename){
        Process p;
        BufferedReader reader;
        try {
            p = Runtime.getRuntime().exec("su");
            DataOutputStream os = new DataOutputStream(p.getOutputStream());
            os.writeBytes("cat "+filename+"\n");
            os.writeBytes("exit\n");
            os.flush();
            p.waitFor();
            reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
        } catch (Exception e) {
            e.printStackTrace();
            reader = new BufferedReader(new StringReader("error"));
        }
        return reader;
    }

このsudoCatメソッドをFileReaderの代わりに使えば良さそう。
さて、変更を加えたこのソースをビルドしたいのだがAndroidStudioのプロジェクトとして開くにはフォルダの構造が違いすぎる。apkのフォルダ構造をAndroidStudioのプロジェクトとして開くプラグインもコンバーターのようなツールも私の知る限りでは存在しない。


なのでapktoolを使う方が楽である。smaliで先ほど書いたsudoCatを書き、FileReaderが使われているところに置換する。ちなみにsmaliとはdalvikバイトコードのアセンブリである。

5行程度だったa()メソッドはsmaliでは以下のようになる。

.method private a(Ljava/lang/String;I)I
    .locals 2
    :try_start_0
    new-instance v0, Ljava/io/BufferedReader;
    new-instance v1, Ljava/io/FileReader;
    invoke-direct {v1, p1}, Ljava/io/FileReader;->(Ljava/lang/String;)V
    invoke-direct {v0, v1}, Ljava/io/BufferedReader;->(Ljava/io/Reader;)V    
    invoke-virtual {v0}, Ljava/io/BufferedReader;->readLine()Ljava/lang/String;
    move-result-object v0
    const/16 v1, 0xa
    invoke-static {v0, v1}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;I)I
    :try_end_0
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
    move-result p2    
    :goto_0
    return p2
    :catch_0
    move-exception v0
    goto :goto_0
.end method

続く

No comments:

Post a Comment