1 /*
2 * Copyright 2017 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 package trebuchet.importers.ftrace
18
19 import trebuchet.io.DataSlice
20 import trebuchet.io.asSlice
21 import trebuchet.model.InvalidId
22 import trebuchet.util.BufferReader
23 import trebuchet.util.StringCache
24 import java.util.regex.Matcher
25 import java.util.regex.Pattern
26 import kotlin.concurrent.getOrSet
27
28 @Suppress("unused")
29 const val FtraceLineRE = """^*(.{1,16})-(\d+) +(?:\( *(\d+)?-*\) )?\[(\d+)] (?:[dX.]...)? *([\d.]*): *([^:]*): (.*)$"""
30
31 class FtraceLine private constructor() {
32 private var _task: String? = null
33 private var _pid: Int = 0
34 private var _tgid: Int = 0
35 private var _cpu: Int = 0
36 private var _timestamp: Double = 0.0
37 private var _function: DataSlice = DataSlice()
38 private var _functionDetails: BufferReader? = null
39
40 val hasTgid: Boolean get() = _tgid != InvalidId
41 var tgid: Int
42 get() = _tgid
43 set(value) {
44 if (hasTgid && _tgid != value) {
45 throw IllegalStateException("tgid fight, currently $_tgid but trying to set $value")
46 }
47 _tgid = value
48 }
49 val task get() = _task
50 val pid get() = _pid
51 val cpu get() = _cpu
52 val timestamp get() = _timestamp
53 val function get() = _function
54 val functionDetailsReader get() = _functionDetails!!
55
setnull56 private fun set(taskName: String?, pid: Int, tgid: Int, cpu: Int, timestamp: Double,
57 func: DataSlice, funcDetails: BufferReader) {
58 _task = taskName
59 _pid = pid
60 _tgid = tgid
61 _cpu = cpu
62 _timestamp = timestamp
63 _function = func
64 _functionDetails = funcDetails
65 }
66
67 companion object {
68 val INVALID_LINE = FtraceLine()
69
70 private val matcherLocal = ThreadLocal<Matcher>()
71 private val readerLocal = ThreadLocal<BufferReader>()
72
73 private val matcher: Matcher
<lambda>null74 get() = matcherLocal.getOrSet { Pattern.compile(FtraceLineRE).matcher("") }
75 private val reader: BufferReader
<lambda>null76 get() = readerLocal.getOrSet { BufferReader() }
77
createnull78 fun create(line: DataSlice, stringCache: StringCache): FtraceLine? {
79 try {
80 reader.read(line, stringCache) {
81 tryMatch(matcher) {
82 val ftraceLine = FtraceLine()
83 ftraceLine.set(
84 string(1),
85 int(2),
86 if (matcher!!.start(3) == -1) InvalidId else int(3),
87 int(4),
88 double(5),
89 slice(6),
90 reader(7)
91 )
92 return@create ftraceLine
93 }
94 }
95 } catch (ex: Exception) {
96
97 }
98 return null
99 }
100 }
101
102 class Parser(val stringCache: StringCache) {
103 private val NullTaskName = stringCache.stringFor("<...>".asSlice())
104 private val ftraceLine = FtraceLine()
105 private val _reader = BufferReader()
106 private val matcher = Pattern.compile(FtraceLineRE).matcher("")
107
parseLine_newnull108 fun parseLine_new(line: DataSlice, callback: (FtraceLine) -> Unit) =
109 _reader.read(line, stringCache) {
110 match(matcher) {
111 ftraceLine.set(
112 string(1),
113 int(2),
114 if (matcher!!.start(3) == -1) InvalidId else int(3),
115 int(4),
116 double(5),
117 slice(6),
118 reader(7)
119 )
120 callback(ftraceLine)
121 }
122 }
123
parseLinenull124 fun parseLine(line: DataSlice, callback: (FtraceLine) -> Unit) =
125 _reader.read(line, stringCache) {
126 var tgid: Int = InvalidId
127 skipChar(' '.toByte())
128 val taskName = stringTo {
129 skipUntil { it == '-'.toByte() }
130 skipUntil { it == '('.toByte() || it == '['.toByte() }
131 rewindUntil { it == '-'.toByte() }
132 }
133 val pid = readInt()
134 skipChar(' '.toByte())
135 if (peek() == '('.toByte()) {
136 skip()
137 if (peek() != '-'.toByte()) {
138 tgid = readInt()
139 }
140 skipUntil { it == ')'.toByte() }
141 }
142 val cpu = readInt()
143 skipCount(2)
144 if (peek() == '.'.toByte() || peek() > '9'.toByte()) {
145 skipCount(5)
146 }
147 skipChar(' '.toByte())
148 val timestamp = readDouble()
149 skipCount(1); skipChar(' '.toByte())
150 val func = sliceTo(ftraceLine.function) { skipUntil { it == ':'.toByte() } }
151 skipCount(1)
152 skipChar(' '.toByte())
153 ftraceLine.set(if (taskName === NullTaskName) null else taskName, pid, tgid, cpu,
154 timestamp, func, _reader)
155 callback(ftraceLine)
156 }
157 }
158 }
159
validnull160 fun FtraceLine.valid() = this !== FtraceLine.INVALID_LINE
161