1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tools.metalava
17 
18 import java.time.LocalDateTime
19 import java.time.format.DateTimeFormatter
20 
21 private val PROGRESS_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss.SSS")
22 private var beginningOfLine = true
23 private var firstProgress = true
24 
25 /** Print a progress message with a timestamp when --verbose is enabled. */
progressnull26 fun progress(message: String) {
27     if (!options.verbose) {
28         return
29     }
30     if (!beginningOfLine) {
31         options.stdout.println()
32     }
33     val now = LocalDateTime.now().format(PROGRESS_TIME_FORMATTER)
34 
35     if (!firstProgress) {
36         options.stdout.print(now)
37         options.stdout.print("   CPU: ")
38         options.stdout.println(getCpuStats())
39 
40         options.stdout.print(now)
41         options.stdout.print("   MEM: ")
42         options.stdout.println(getMemoryStats())
43     }
44     firstProgress = false
45 
46     options.stdout.print(now)
47     options.stdout.print(" ")
48     options.stdout.print(message)
49     options.stdout.flush()
50     beginningOfLine = message.endsWith('\n')
51 }
52 
53 private var lastMillis: Long = -1L
54 private var lastUserMillis: Long = -1L
55 private var lastCpuMillis: Long = -1L
56 
getCpuStatsnull57 private fun getCpuStats(): String {
58     val nowMillis = System.currentTimeMillis()
59     val userMillis = threadMXBean.getCurrentThreadUserTime() / 1000_000
60     val cpuMillis = threadMXBean.getCurrentThreadCpuTime() / 1000_000
61 
62     if (lastMillis == -1L) {
63         lastMillis = nowMillis
64     }
65     if (lastUserMillis == -1L) {
66         lastUserMillis = userMillis
67     }
68     if (lastCpuMillis == -1L) {
69         lastCpuMillis = cpuMillis
70     }
71 
72     val realDeltaMs = nowMillis - lastMillis
73     val userDeltaMillis = userMillis - lastUserMillis
74     // Sometimes we'd get "-0.0" without the max.
75     val sysDeltaMillis = Math.max(0, cpuMillis - lastCpuMillis - userDeltaMillis)
76 
77     lastMillis = nowMillis
78     lastUserMillis = userMillis
79     lastCpuMillis = cpuMillis
80 
81     return String.format(
82         "+%.1freal +%.1fusr +%.1fsys",
83         realDeltaMs / 1_000.0,
84         userDeltaMillis / 1_000.0,
85         sysDeltaMillis / 1_000.0)
86 }
87 
getMemoryStatsnull88 private fun getMemoryStats(): String {
89     val mu = memoryMXBean.getHeapMemoryUsage()
90 
91     return String.format(
92         "%dmi %dmu %dmc %dmx",
93         mu.init / 1024 / 1024,
94         mu.used / 1024 / 1024,
95         mu.committed / 1024 / 1024,
96         mu.max / 1024 / 1024)
97 }
98 
99 /** Used for verbose output to show progress bar */
100 private var tick = 0
101 
102 /** Needed for tests to ensure we don't get unpredictable behavior of "." in output */
resetTickernull103 fun resetTicker() {
104     tick = 0
105 }
106 
107 /** Print progress */
ticknull108 fun tick() {
109     tick++
110     if (tick % 100 == 0) {
111         if (!options.verbose) {
112             return
113         }
114         beginningOfLine = false
115         options.stdout.print(".")
116         options.stdout.flush()
117     }
118 }
119