1 /*
<lambda>null2 * Copyright 2019 Google Inc.
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 * https://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
17 /*
18 * Notes
19 *
20 * TODO (felipeal): generate .csv file
21 * TODO (felipeal): automatically generate atrace / restart system_server
22 * TODO (felipeal): add mre command-line options, like threshold value
23 */
24
25 /*
26 * Imports
27 */
28
29 import java.io.File
30 import trebuchet.model.Model
31 import trebuchet.extras.parseTrace
32 import trebuchet.model.base.Slice
33 import trebuchet.model.base.SliceGroup
34 import trebuchet.queries.slices.slices
35 import java.io.PrintStream
36
37 /*
38 * Constants
39 */
40
41 /*
42 * Class Definition
43 */
44
45 /*
46 * Class Extensions
47 */
48
49 /*
50 * Helper Functions
51 */
52
53 inline fun <reified T> Slice?.ifGroup(func: (SliceGroup) -> T): T? {
54 if (this is SliceGroup) {
55 return func(this)
56 }
57 return null
58 }
59
Doublenull60 fun Double.durationString(): String {
61 return "%.3f ms".format(this * 1000)
62 }
63
printLinenull64 fun printLine(output: PrintStream, name: String, duration: Double, csvFormat: Boolean = false) {
65 if (csvFormat) {
66 output.println("${name};${duration * 1000}")
67 } else {
68 output.println("${name}: ${duration.durationString()}")
69 }
70 }
71
printLinenull72 fun printLine(output: PrintStream, model: SliceGroup, csvFormat: Boolean = false) {
73 printLine(output, model.name, model.duration, csvFormat)
74 }
75
measureServiceStartupnull76 fun measureServiceStartup(model: Model, thresholdMs: Int = 0, output: PrintStream = System.out, csvFormat: Boolean = false, otherName: String = "Other") {
77 model.slices().firstOrNull {
78 it.name == "StartServices" && it is SliceGroup
79 }.ifGroup {
80 printLine(output, "StartServices", it.duration, csvFormat);
81 val childDurations = it.children.sumByDouble { service ->
82 if (service.duration * 1000 >= thresholdMs) {
83 printLine(output, service, csvFormat)
84 service.duration
85 } else {
86 0.0
87 }
88 }
89 printLine(output, "Other", it.duration - childDurations, csvFormat);
90 }
91 }
92
93 /*
94 * Main Function
95 */
96
mainnull97 fun main(args: Array<String>) {
98 if (args.isEmpty()) {
99 println("Usage: SystemServerAnalyzerKt <trace_filename> [-t threshold_ms] [-o output_filename]")
100 return
101 }
102
103 val input = args[0]
104
105 println("Opening ${input}")
106 val trace = parseTrace(File(input), verbose = true)
107
108 var csvFormat = false
109 var output = System.out
110 var thresholdMs = 5;
111
112 // Parse optional arguments
113 var nextArg = 1
114 while (nextArg < args.size) {
115 var arg = args[nextArg++]
116 var value = args[nextArg++]
117 when (arg) {
118 "-t" -> thresholdMs = value.toInt()
119 "-o" -> {
120 output = PrintStream(File(value).outputStream())
121 csvFormat = true
122 println("Writing CSV output to ${value}")
123 }
124 else -> println("invalid option: ${arg}")
125 }
126 }
127
128 measureServiceStartup(trace, thresholdMs, output, csvFormat)
129 }