Skip to content

Commit cf45bc8

Browse files
Merge pull request #117 from Grigory-Rylov/simpleperf_test
Simpleperf test
2 parents ff59ab8 + 480a99c commit cf45bc8

32 files changed

Lines changed: 2516 additions & 543 deletions

File tree

app/dist_files/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<key>CFBundleVersion</key>
4949
<string>100</string>
5050
<key>NSHumanReadableCopyright</key>
51-
<string>Copyright (C) 2020</string>
51+
<string>Copyright (C) 2024</string>
5252
<key>NSHighResolutionCapable</key>
5353
<string>true</string>
5454
</dict>

app/src/main/java/com/github/grishberg/profiler/common/settings/JsonSettings.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,6 @@ class JsonSettings(
342342
override fun filesDir() = filesDirName
343343

344344
override var shouldShowToolbar: Boolean = false
345-
override var lastVersion: String = ""
346345

347346
private fun initWithDefaults() {
348347
initWithDefaultStringValue(SETTINGS_FONT_NAME, "Arial")

app/src/test/java/com/github/grishberg/profiler/chart/CalledStacktraceTest.kt

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
package com.github.grishberg.profiler.chart
22

3-
import com.github.grishberg.profiler.core.ProfileData
43
import com.github.grishberg.profiler.analyzer.ProfileDataImpl
54
import com.github.grishberg.profiler.common.AppLogger
5+
import com.github.grishberg.profiler.core.ProfileData
66
import com.nhaarman.mockitokotlin2.mock
7-
import org.junit.Assert.assertEquals
8-
import org.junit.Test
97
import java.awt.FontMetrics
108
import java.awt.Graphics2D
119
import java.awt.geom.AffineTransform
10+
import org.junit.Assert.assertEquals
11+
import org.junit.Test
1212

1313
internal class CalledStacktraceTest {
14+
1415
val callers = mutableListOf<ProfileData>()
1516
val callTraceItems = mutableListOf<ProfileData>()
1617
val renderer = object : SelectionRenderer {
17-
override var currentThreadId: Int = -1
18+
private var _currentThreadId: Int = -1
19+
override val currentThreadId: Int
20+
get() = _currentThreadId
21+
22+
override fun setCurrentThreadId(id: Int) {
23+
_currentThreadId = id
24+
}
1825

1926
override fun draw(
2027
g: Graphics2D,
@@ -57,23 +64,18 @@ internal class CalledStacktraceTest {
5764
val createdStubDrawable = profileData("stub.CustomPanelStubDrawable.<init>")
5865
val createdChildDimensionProvider = profileData("common.DimensionProvider.<init>")
5966
val caller = profileData("panel.CustomPanelController.<init>")
60-
val parent = profileData("MainActivity.onCreate")
61-
.child("di.DaggerCustomPanelComponent.getPanelController").apply {
67+
val parent = profileData("MainActivity.onCreate").child("di.DaggerCustomPanelComponent.getPanelController").apply {
6268
child("panel.CustomPanelDataSource.<init>")
63-
child("dagger.internal.DoubleCheck.get")
64-
.child("di.CustomPanelModule_ProvideCustomPanelViewFactory.get")
69+
child("dagger.internal.DoubleCheck.get").child("di.CustomPanelModule_ProvideCustomPanelViewFactory.get")
6570
.child("di.CustomPanelModule_ProvideCustomPanelViewFactory.get").apply {
6671
child("common.DimensionProvider_Factory.get").child("common.DimensionProvider_Factory.get")
67-
.child("common.DimensionProvider_Factory.newInstance")
68-
.addChild(createdChildDimensionProvider)
72+
.child("common.DimensionProvider_Factory.newInstance").addChild(createdChildDimensionProvider)
6973
child("di.CustomPanelModule_ProvideCustomPanelViewFactory.provideCustomPanelView").apply {
70-
child("di.CustomPanelModule.provideCustomPanelView")
71-
.addChild(customPanelView)
74+
child("di.CustomPanelModule.provideCustomPanelView").addChild(customPanelView)
7275
child("dagger.internal.Preconditions.checkNotNull")
7376
}
7477
}
75-
child("di.DaggerCustomPanelComponent.getCustomPanelStubDrawable")
76-
.addChild(createdStubDrawable)
78+
child("di.DaggerCustomPanelComponent.getCustomPanelStubDrawable").addChild(createdStubDrawable)
7779
addChild(caller)
7880
}
7981

app/src/test/java/com/github/grishberg/profiler/comparator/TraceProfileDataSearchInfoTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class TraceProfileDataSearchInfoTest {
4343
override val startTimeUs = 0L
4444
override val threadTimeBounds: Map<Int, ThreadTimeBounds> = emptyMap()
4545
override val threads = listOf(ThreadItemImpl("test", 0))
46+
override val minThreadTime: Double = 0.0
47+
override val minGlobalTime: Double = 0.0
4648
}
4749

4850
private val underTest = TraceProfileDataFinder(trace)

core/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ repositories {
1212
}
1313

1414
dependencies {
15+
implementation 'com.github.Grishberg:simpleperf-parser:1.0.6'
1516
implementation 'com.github.Grigory-Rylov:adb-facade-core:0.1.8'
1617
implementation 'com.github.Grigory-Rylov:andoid_method_trace_recorder:2.1.0'
1718
implementation 'com.github.Grigory-Rylov:proguard-deobfuscator:0.4.0'

core/src/main/java/com/github/grishberg/profiler/analyzer/AnalyzerResultImpl.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ data class AnalyzerResultImpl(
88
override val threadTimeBounds: Map<Int, ThreadTimeBoundsImpl>,
99
override val globalTimeBounds: Map<Int, ThreadTimeBoundsImpl>,
1010
override val maxLevel: Int,
11-
internal val mutableData: MutableMap<Int, MutableList<ProfileDataImpl>>,
11+
internal val mutableData: Map<Int, List<ProfileDataImpl>>,
1212
override val threads: List<ThreadItemImpl>,
1313
override val mainThreadId: Int,
1414
override val startTimeUs: Long = -1,
15+
override val minThreadTime: Double = 0.0,
16+
override val minGlobalTime: Double = 0.0,
1517
) : AnalyzerResult {
18+
1619
override val data: Map<Int, List<ProfileData>>
1720
get() = mutableData
1821
}

core/src/main/java/com/github/grishberg/profiler/analyzer/ProfileDataImpl.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.grishberg.profiler.analyzer
22

3+
import com.github.grishberg.profiler.core.ExtendedData
34
import com.github.grishberg.profiler.core.ProfileData
45

56
data class ProfileDataImpl(
@@ -11,7 +12,8 @@ data class ProfileDataImpl(
1112
override var globalEndTimeInMillisecond: Double = -1.0,
1213
override var threadSelfTime: Double = 0.0,
1314
override var globalSelfTime: Double = 0.0,
14-
override var parent: ProfileDataImpl? = null
15+
override var parent: ProfileDataImpl? = null,
16+
override var extendedData: ExtendedData? = null,
1517
) : ProfileData {
1618
private val _children = mutableListOf<ProfileDataImpl>()
1719
override val children: List<ProfileDataImpl> = _children
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package com.github.grishberg.profiler.analyzer
2+
3+
import com.android.tools.profilers.cpu.CaptureNode
4+
import com.android.tools.profilers.cpu.CpuCapture
5+
import com.android.tools.profilers.cpu.CpuThreadInfo
6+
import com.android.tools.profilers.cpu.nodemodel.CppFunctionModel
7+
import com.android.tools.profilers.cpu.nodemodel.JavaMethodModel
8+
import com.android.tools.profilers.cpu.nodemodel.NoSymbolModel
9+
import com.android.tools.profilers.cpu.nodemodel.SyscallModel
10+
import com.android.tools.profilers.cpu.simpleperf.SimpleperfTraceParser
11+
import com.github.grishberg.profiler.analyzer.converter.NameConverter
12+
import com.github.grishberg.profiler.common.AppLogger
13+
import com.github.grishberg.profiler.core.AnalyzerResult
14+
import com.github.grishberg.profiler.core.ExtendedData
15+
import java.io.File
16+
import kotlin.math.max
17+
import kotlin.math.min
18+
import kotlin.random.Random
19+
20+
class SimplePerfAnalyzer(
21+
private val log: AppLogger,
22+
private val nameConverter: NameConverter,
23+
) {
24+
25+
fun analyze(traceFile: File): AnalyzerResult {
26+
val parser = SimpleperfTraceParser()
27+
val traceId = Random.nextLong()
28+
try {
29+
return captureToAnalyzerResult(parser.parse(traceFile, traceId))
30+
} catch (e: Exception) {
31+
throw WrongFormatException()
32+
}
33+
}
34+
35+
private fun captureToAnalyzerResult(capture: CpuCapture): AnalyzerResult {
36+
val threads = mapThreads(capture.threads)
37+
38+
val data = mutableMapOf<Int, List<ProfileDataImpl>>()
39+
var maxLevel = 0
40+
val threadTimeBounds = mutableMapOf<Int, ThreadTimeBoundsImpl>()
41+
val globalTimeBounds = mutableMapOf<Int, ThreadTimeBoundsImpl>()
42+
43+
var minThreadTime = Long.MAX_VALUE
44+
var minGlobalTime = Long.MAX_VALUE
45+
46+
for (thread in threads) {
47+
val currentThreadRoot = capture.getCaptureNode(thread.threadId) ?: continue
48+
val profileDataList = mutableListOf<ProfileDataImpl>()
49+
for (threadChild in currentThreadRoot.children) {
50+
val newRoot = mapCpuNode(threadChild)
51+
val level = processChildren(profileDataList, threadChild, newRoot)
52+
maxLevel = max(maxLevel, level)
53+
}
54+
threadTimeBounds[thread.threadId] = ThreadTimeBoundsImpl(
55+
minTime = convertTime(currentThreadRoot.startThread), maxTime = convertTime(currentThreadRoot.endThread)
56+
)
57+
globalTimeBounds[thread.threadId] = ThreadTimeBoundsImpl(
58+
minTime = convertTime(currentThreadRoot.startGlobal), maxTime = convertTime(currentThreadRoot.endGlobal)
59+
)
60+
61+
minThreadTime = min(minThreadTime, currentThreadRoot.startThread)
62+
minGlobalTime = min(minGlobalTime, currentThreadRoot.startGlobal)
63+
data[thread.threadId] = profileDataList
64+
}
65+
66+
return AnalyzerResultImpl(
67+
threadTimeBounds = threadTimeBounds,
68+
globalTimeBounds = globalTimeBounds,
69+
maxLevel = maxLevel,
70+
mutableData = data,
71+
threads = threads,
72+
mainThreadId = capture.mainThreadId,
73+
startTimeUs = -1,
74+
convertTime(minThreadTime),
75+
convertTime(minGlobalTime),
76+
)
77+
}
78+
79+
private fun processChildren(allNodes: MutableList<ProfileDataImpl>, node: CaptureNode, newRoot: ProfileDataImpl): Int {
80+
var maxLevel = node.depth - 1
81+
node.children.forEachIndexed { index, captureNode ->
82+
val profileDataChild = mapCpuNode(captureNode)
83+
newRoot.addChild(profileDataChild)
84+
allNodes.add(newRoot)
85+
86+
val level = processChildren(allNodes, captureNode, profileDataChild)
87+
maxLevel = max(maxLevel, level)
88+
89+
}
90+
return maxLevel
91+
}
92+
93+
private fun mapThreads(threads: Set<CpuThreadInfo>): List<ThreadItemImpl> {
94+
return threads.map { ThreadItemImpl(it.name, it.id) }.sortedBy { it.threadId }
95+
}
96+
97+
private fun mapCpuNode(cpuNode: CaptureNode): ProfileDataImpl {
98+
val data = cpuNode.data
99+
val profileData = ProfileDataImpl(
100+
name = data.fullName,
101+
level = cpuNode.depth - THREAD_DEPTH_OFFSET,
102+
threadStartTimeInMillisecond = convertTime(cpuNode.startThread),
103+
threadEndTimeInMillisecond = convertTime(cpuNode.endThread),
104+
globalStartTimeInMillisecond = convertTime(cpuNode.startGlobal),
105+
globalEndTimeInMillisecond = convertTime(cpuNode.endGlobal),
106+
)
107+
when (data) {
108+
is CppFunctionModel -> {
109+
profileData.extendedData = ExtendedData.CppFunctionData(
110+
tag = data.tag,
111+
id = data.id,
112+
fullName = data.fullName,
113+
classOrNamespace = data.classOrNamespace,
114+
parameters = data.parameters,
115+
isUserCode = data.isUserCode,
116+
fileName = data.fileName,
117+
vAddress = data.vAddress,
118+
)
119+
}
120+
121+
is JavaMethodModel -> {
122+
profileData.extendedData = ExtendedData.JavaMethodData(
123+
tag = data.tag,
124+
id = data.id,
125+
fullName = data.fullName,
126+
className = data.className,
127+
signature = data.signature,
128+
)
129+
}
130+
131+
is NoSymbolModel -> {
132+
profileData.extendedData = ExtendedData.NoSymbolData(
133+
tag = data.tag,
134+
id = data.id,
135+
fullName = data.fullName,
136+
isKernel = data.isKernel,
137+
)
138+
}
139+
140+
is SyscallModel -> {
141+
profileData.extendedData = ExtendedData.SyscallData(
142+
tag = data.tag,
143+
id = data.id,
144+
fullName = data.fullName,
145+
)
146+
}
147+
}
148+
149+
return profileData
150+
}
151+
152+
private fun convertTime(time: Long): Double {
153+
return time.toDouble() / 1000.0
154+
}
155+
156+
private companion object {
157+
private const val THREAD_DEPTH_OFFSET = 1
158+
}
159+
}

0 commit comments

Comments
 (0)