Skip to content

Commit

Permalink
Add --execution-sample-type option
Browse files Browse the repository at this point in the history
  • Loading branch information
parttimenerd committed Apr 12, 2024
1 parent f53b7d3 commit 790771b
Show file tree
Hide file tree
Showing 10 changed files with 28 additions and 27 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- "main"
workflow_dispatch:

jobs:
pre-release:
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## Unreleased

- Fix handling of files without execution samplesqq
- Fix handling of files without execution samples
- (see https://github.com/parttimenerd/intellij-profiler-plugin/issues/30)
- Add `--execution-sample-type` to specify the type of execution samples to use

## [0.0.4]

Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/me/bechberger/jfrtofp/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ class Main : Callable<Int> {
?: Path.of(file.toString().replace(".jfr", ".json")),
(
when (mode) {
"speedscope" -> SpeedscopeGenerator(file)
"d3-flamegraph" -> D3FlamegraphGenerator(file)
"speedscope" -> SpeedscopeGenerator(file, config.toConfig())
"d3-flamegraph" -> D3FlamegraphGenerator(file, config.toConfig())
else -> throw IllegalArgumentException("Unknown mode $mode")
}
).generate().toByteArray(),
Expand Down
8 changes: 5 additions & 3 deletions src/main/kotlin/me/bechberger/jfrtofp/other/BaseGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ import me.bechberger.jfrtofp.util.sampledThreadOrNull
import me.bechberger.jfrtofp.util.toMicros
import java.nio.file.Path
import java.util.Objects
import me.bechberger.jfrtofp.processor.Config
import me.bechberger.jfrtofp.processor.ConfigMixin

internal val jsonFormat =
Json {
prettyPrint = false
encodeDefaults = true
}

abstract class BaseGenerator(jfrFile: Path) {
abstract class BaseGenerator(jfrFile: Path, val config: Config) {
private val events = RecordingFile.readAllEvents(jfrFile)

internal fun List<RecordedEvent>.perThread(): Map<RecordedThread, List<RecordedEvent>> = groupBy { it.sampledThread }
Expand Down Expand Up @@ -85,9 +87,9 @@ abstract class BaseGenerator(jfrFile: Path) {

abstract fun generate(): String

fun isExecutionSample(event: RecordedEvent) = config.executionSampleType.matches(event.eventType.name)

companion object {
fun isExecutionSample(event: RecordedEvent) =
event.eventType.name.equals("jdk.ExecutionSample") || event.eventType.name.equals("jdk.NativeMethodSample")

fun shortMethodString(method: RecordedMethod): String {
val nameParts = method.type.name.split(".")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import me.bechberger.jfrtofp.util.realJavaName
import me.bechberger.jfrtofp.util.sampledThread
import java.nio.file.Path
import java.util.IdentityHashMap
import me.bechberger.jfrtofp.processor.Config

class D3FlamegraphGenerator(jfrFile: Path) : BaseGenerator(jfrFile) {
class D3FlamegraphGenerator(jfrFile: Path, config: Config) : BaseGenerator(jfrFile, config) {
/**
* Returns a JSON string for https://github.com/spiermar/d3-flame-graph, entries have the format
* <code>
Expand Down Expand Up @@ -47,8 +48,7 @@ class D3FlamegraphGenerator(jfrFile: Path) : BaseGenerator(jfrFile) {
val threads = mutableMapOf<RecordedThread, Node>()
for (sample in samples) {
assert(
sample.eventType.name.equals("jdk.ExecutionSample") ||
sample.eventType.name.equals("jdk.NativeMethodSample"),
config.isExecutionSample(sample)
)
if (sample.stackTrace.frames.isEmpty()) {
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import kotlinx.serialization.encodeToString
import me.bechberger.jfrtofp.util.estimateMinInterval
import me.bechberger.jfrtofp.util.realJavaName
import java.nio.file.Path
import me.bechberger.jfrtofp.processor.Config
import kotlin.math.max
import kotlin.math.roundToLong

Expand Down Expand Up @@ -84,7 +85,7 @@ internal object Speedscope {
}
}

class SpeedscopeGenerator(jfrFile: Path) : BaseGenerator(jfrFile) {
class SpeedscopeGenerator(jfrFile: Path, config: Config) : BaseGenerator(jfrFile, config) {
/**
* Returns the traces in a format suitable for speedscope
* (https://github.com/jlfwong/speedscope/blob/main/src/lib/file-format-spec.ts)
Expand Down
8 changes: 8 additions & 0 deletions src/main/kotlin/me/bechberger/jfrtofp/processor/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,16 @@ class ConfigMixin {
@CommandLine.Option(names = ["--source-url"], description = ["Source url to use in the profile for Firefox Profiler"])
var sourceUrl: String? = null

@CommandLine.Option(names = ["--execution-sample-type"], description = ["Glob pattern (* and | supported) that matches the used execution sample type"])
var executionSampleType: String = "jdk.ExecutionSample|jdk.NativeMethodSample"

fun toConfig() =
Config(
nonProjectPackagePrefixes = nonProjectPackagePrefixes,
maxExecutionSamplesPerThread = maxExecutionSamplesPerThread,
maxMiscSamplesPerThread = maxMiscSamplesPerThread,
sourceUrl = sourceUrl,
executionSampleType = executionSampleType.replace(".", "\\.").replace("*", ".*").toRegex()
)

companion object {
Expand Down Expand Up @@ -121,7 +125,11 @@ data class Config(
val ignoredEvents: Set<String> = DEFAULT_IGNORED_EVENTS.toSet(),
/** minimum number of samples or markers a event has to have */
val minRequiredItemsPerThread: Int = DEFAULT_MIN_ITEMS_PER_THREAD,
val executionSampleType: Regex = "jdk.ExecutionSample|jdk.NativeMethodSample".toRegex(),
) {
fun isExecutionSample(event: RecordedEvent) = isExecutionSample(event.eventType.name)
fun isExecutionSample(eventType: String) = executionSampleType.matches(eventType)

companion object {
val DEFAULT_ADDED_MEMORY_PROPERTIES = listOf(MemoryProperty.USED_HEAP, MemoryProperty.COMMITTED_HEAP)
const val DEFAULT_INITIAL_VISIBLE_THREADS = 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,7 @@ class MarkerSchemaProcessor(val config: Config) {
private val timelineMemoryEvents = setOf("memory", "gc", "GarbageCollection")

private fun isIgnoredEvent(event: String) =
event.equals("jdk.ExecutionSample") ||
event.equals(
"jdk.NativeMethodSample",
)
config.isExecutionSample(event)

private fun isIgnoredField(field: ValueDescriptor) =
(config.omitEventThreadProperty && field.name == "eventThread") ||
Expand Down
7 changes: 3 additions & 4 deletions src/main/kotlin/me/bechberger/jfrtofp/processor/Processor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import me.bechberger.jfrtofp.util.BasicJSONGenerator
import me.bechberger.jfrtofp.util.Percentage
import me.bechberger.jfrtofp.util.encodeToJSONStream
import me.bechberger.jfrtofp.util.estimateIntervalInMillis
import me.bechberger.jfrtofp.util.isExecutionSample
import me.bechberger.jfrtofp.util.isGCThread
import me.bechberger.jfrtofp.util.isSystemThread
import me.bechberger.jfrtofp.util.jsonFormat
Expand Down Expand Up @@ -225,7 +224,7 @@ class ThreadProcessor(
thread = it
}
}
if (event.isExecutionSample) {
if (config.isExecutionSample(event)) {
processExecutionSample(event)
_items++
} else {
Expand Down Expand Up @@ -593,7 +592,7 @@ data class BasicInformation(
cpuInformation = event
} else if (osInformation == null && event.eventType.name == "jdk.OSInformation") {
osInformation = event
} else if (event.isExecutionSample) {
} else if (config.isExecutionSample(event)) {
if (backupMainThreadId == null) {
backupMainThreadId = event.sampledThread.id
backupStartTime = event.startTime
Expand Down Expand Up @@ -855,7 +854,7 @@ internal class MetaProcessor(
gcThreads.add(it.id)
}
}
if (event.isExecutionSample) {
if (config.isExecutionSample(event)) {
threadInfo.executionSampleCount++
} else {
threadInfo.otherSampleCount++
Expand Down
10 changes: 1 addition & 9 deletions src/main/kotlin/me/bechberger/jfrtofp/util/jfr.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ fun estimateIntervalInMillis(startTimesPerThread: Map<Long, List<Milliseconds>>)
return startTimesPerThread.values.estimateInterval()
}

val RecordedEvent.isExecutionSample
get() = eventType.name.equals("jdk.ExecutionSample") || eventType.name.equals("jdk.NativeMethodSample")

val RecordedClass.pkg
get() =
name.split("$")[0].split(".").let {
Expand Down Expand Up @@ -132,16 +129,11 @@ val RecordedThread.name: String?

fun RecordedThread.isGCThread() = !isVirtualThread() && osName.startsWith("GC Thread") && javaName == null

private val RecordedEvent.isSampledThreadCorrectProperty
get() = eventType.name == "jdk.NativeMethodSample" || eventType.name == "jdk.ExecutionSample"

val RecordedEvent.sampledThread: RecordedThread
get() = sampledThreadOrNull!!

val RecordedEvent.sampledThreadOrNull: RecordedThread?
get() = if (isSampledThreadCorrectProperty) getThread("sampledThread") else thread

fun List<RecordedEvent>.groupByType() = groupBy { if (it.eventType.name == "jdk.NativeMethodSample") "jdk.ExecutionSample" else it.eventType.name }
get() = if (this.hasField("sampledThread")) getThread("sampledThread") else thread

val RecordedEvent.realThread: RecordedThread?
get() = thread ?: sampledThreadOrNull ?: (if (hasField("thread")) getThread("thread") else null)
Expand Down

0 comments on commit 790771b

Please sign in to comment.