Skip to content

Commit

Permalink
#83 simpleperf viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
Grigory-Rylov committed Aug 22, 2024
1 parent ff59ab8 commit 0ddf21e
Show file tree
Hide file tree
Showing 25 changed files with 858 additions and 396 deletions.
2 changes: 1 addition & 1 deletion app/dist_files/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
<key>CFBundleVersion</key>
<string>100</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (C) 2020</string>
<string>Copyright (C) 2024</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
</dict>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,6 @@ class JsonSettings(
override fun filesDir() = filesDirName

override var shouldShowToolbar: Boolean = false
override var lastVersion: String = ""

private fun initWithDefaults() {
initWithDefaultStringValue(SETTINGS_FONT_NAME, "Arial")
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ repositories {
}

dependencies {
implementation 'com.github.Grishberg:simpleperf-parser:1.0.6'
implementation 'com.github.Grigory-Rylov:adb-facade-core:0.1.8'
implementation 'com.github.Grigory-Rylov:andoid_method_trace_recorder:2.1.0'
implementation 'com.github.Grigory-Rylov:proguard-deobfuscator:0.4.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ data class AnalyzerResultImpl(
override val threadTimeBounds: Map<Int, ThreadTimeBoundsImpl>,
override val globalTimeBounds: Map<Int, ThreadTimeBoundsImpl>,
override val maxLevel: Int,
internal val mutableData: MutableMap<Int, MutableList<ProfileDataImpl>>,
internal val mutableData: Map<Int, List<ProfileDataImpl>>,
override val threads: List<ThreadItemImpl>,
override val mainThreadId: Int,
override val startTimeUs: Long = -1,
override val minThreadTime: Double = 0.0,
override val minGlobalTime: Double = 0.0,
) : AnalyzerResult {

override val data: Map<Int, List<ProfileData>>
get() = mutableData
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.grishberg.profiler.analyzer

import com.github.grishberg.profiler.core.ExtendedData
import com.github.grishberg.profiler.core.ProfileData

data class ProfileDataImpl(
Expand All @@ -11,7 +12,8 @@ data class ProfileDataImpl(
override var globalEndTimeInMillisecond: Double = -1.0,
override var threadSelfTime: Double = 0.0,
override var globalSelfTime: Double = 0.0,
override var parent: ProfileDataImpl? = null
override var parent: ProfileDataImpl? = null,
override var extendedData: ExtendedData? = null,
) : ProfileData {
private val _children = mutableListOf<ProfileDataImpl>()
override val children: List<ProfileDataImpl> = _children
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.github.grishberg.profiler.analyzer

import com.android.tools.profilers.cpu.CaptureNode
import com.android.tools.profilers.cpu.CpuCapture
import com.android.tools.profilers.cpu.CpuThreadInfo
import com.android.tools.profilers.cpu.nodemodel.CppFunctionModel
import com.android.tools.profilers.cpu.nodemodel.JavaMethodModel
import com.android.tools.profilers.cpu.nodemodel.NoSymbolModel
import com.android.tools.profilers.cpu.nodemodel.SyscallModel
import com.android.tools.profilers.cpu.simpleperf.SimpleperfTraceParser
import com.github.grishberg.profiler.analyzer.converter.NameConverter
import com.github.grishberg.profiler.common.AppLogger
import com.github.grishberg.profiler.core.AnalyzerResult
import com.github.grishberg.profiler.core.ExtendedData
import java.io.File
import kotlin.math.max
import kotlin.math.min
import kotlin.random.Random

class SimplePerfAnalyzer(
private val log: AppLogger,
private val nameConverter: NameConverter,
) {

fun analyze(traceFile: File): AnalyzerResult {
val parser = SimpleperfTraceParser()
val traceId = Random.nextLong()
try {
return captureToAnalyzerResult(parser.parse(traceFile, traceId))
} catch (e: Exception) {
throw WrongFormatException()
}
}

private fun captureToAnalyzerResult(capture: CpuCapture): AnalyzerResult {
val threads = mapThreads(capture.threads)

val data = mutableMapOf<Int, List<ProfileDataImpl>>()
var maxLevel = 0
val threadTimeBounds = mutableMapOf<Int, ThreadTimeBoundsImpl>()
val globalTimeBounds = mutableMapOf<Int, ThreadTimeBoundsImpl>()

var minThreadTime = Long.MAX_VALUE
var minGlobalTime = Long.MAX_VALUE

for (thread in threads) {
val currentThreadRoot = capture.getCaptureNode(thread.threadId) ?: continue
val profileDataList = mutableListOf<ProfileDataImpl>()
for (threadChild in currentThreadRoot.children) {
val newRoot = mapCpuNode(threadChild)
val level = processChildren(profileDataList, threadChild, newRoot)
maxLevel = max(maxLevel, level)
}
threadTimeBounds[thread.threadId] = ThreadTimeBoundsImpl(
minTime = convertTime(currentThreadRoot.startThread), maxTime = convertTime(currentThreadRoot.endThread)
)
globalTimeBounds[thread.threadId] = ThreadTimeBoundsImpl(
minTime = convertTime(currentThreadRoot.startGlobal), maxTime = convertTime(currentThreadRoot.endGlobal)
)

minThreadTime = min(minThreadTime, currentThreadRoot.startThread)
minGlobalTime = min(minGlobalTime, currentThreadRoot.startGlobal)
data[thread.threadId] = profileDataList
}

return AnalyzerResultImpl(
threadTimeBounds = threadTimeBounds,
globalTimeBounds = globalTimeBounds,
maxLevel = maxLevel,
mutableData = data,
threads = threads,
mainThreadId = capture.mainThreadId,
startTimeUs = -1,
convertTime(minThreadTime),
convertTime(minGlobalTime),
)
}

private fun processChildren(allNodes: MutableList<ProfileDataImpl>, node: CaptureNode, newRoot: ProfileDataImpl): Int {
var maxLevel = node.depth - 1
node.children.forEachIndexed { index, captureNode ->
val profileDataChild = mapCpuNode(captureNode)
newRoot.addChild(profileDataChild)
allNodes.add(newRoot)

val level = processChildren(allNodes, captureNode, profileDataChild)
maxLevel = max(maxLevel, level)

}
return maxLevel
}

private fun mapThreads(threads: Set<CpuThreadInfo>): List<ThreadItemImpl> {
return threads.map { ThreadItemImpl(it.name, it.id) }.sortedBy { it.threadId }
}

private fun mapCpuNode(cpuNode: CaptureNode): ProfileDataImpl {
val data = cpuNode.data
val profileData = ProfileDataImpl(
name = data.fullName,
level = cpuNode.depth - THREAD_DEPTH_OFFSET,
threadStartTimeInMillisecond = convertTime(cpuNode.startThread),
threadEndTimeInMillisecond = convertTime(cpuNode.endThread),
globalStartTimeInMillisecond = convertTime(cpuNode.startGlobal),
globalEndTimeInMillisecond = convertTime(cpuNode.endGlobal),
)
when (data) {
is CppFunctionModel -> {
profileData.extendedData = ExtendedData.CppFunctionData(
tag = data.tag,
id = data.id,
fullName = data.fullName,
classOrNamespace = data.classOrNamespace,
parameters = data.parameters,
isUserCode = data.isUserCode,
fileName = data.fileName,
vAddress = data.vAddress,
)
}

is JavaMethodModel -> {
profileData.extendedData = ExtendedData.JavaMethodData(
tag = data.tag,
id = data.id,
fullName = data.fullName,
className = data.className,
signature = data.signature,
)
}

is NoSymbolModel -> {
profileData.extendedData = ExtendedData.NoSymbolData(
tag = data.tag,
id = data.id,
fullName = data.fullName,
isKernel = data.isKernel,
)
}

is SyscallModel -> {
profileData.extendedData = ExtendedData.SyscallData(
tag = data.tag,
id = data.id,
fullName = data.fullName,
)
}
}

return profileData
}

private fun convertTime(time: Long): Double {
return time.toDouble() / 1000.0
}

private companion object {
private const val THREAD_DEPTH_OFFSET = 1
}
}
Loading

0 comments on commit 0ddf21e

Please sign in to comment.