Skip to content

Commit 18f6d01

Browse files
committed
Flamegraph sub-aggregation by thread name
1 parent 0ecc23c commit 18f6d01

File tree

5 files changed

+68
-35
lines changed

5 files changed

+68
-35
lines changed

x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/GetStackTracesResponseBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ public GetStackTracesResponse build() {
165165
StackTrace stackTrace = entry.getValue();
166166
stackTrace.count = event.count;
167167
stackTrace.executableName = event.executableName;
168+
stackTrace.threadName = event.threadName;
168169
if (event.subGroups != null) {
169170
stackTrace.subGroups = event.subGroups;
170171
}

x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/StackTrace.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ final class StackTrace implements ToXContentObject {
3131
double annualCostsUSD;
3232
long count;
3333
String executableName;
34+
String threadName;
3435

3536
StackTrace(
3637
int[] addressOrLines,
@@ -49,6 +50,7 @@ final class StackTrace implements ToXContentObject {
4950
this.annualCostsUSD = annualCostsUSD;
5051
this.count = count;
5152
this.executableName = "";
53+
this.threadName = "";
5254
}
5355

5456
private static final int BASE64_FRAME_ID_LENGTH = 32;
@@ -232,6 +234,14 @@ public String getExecutableName() {
232234
return executableName;
233235
}
234236

237+
public String getThreadName() {
238+
if (threadName.isEmpty()) {
239+
// kernel threads are not associated with an executable
240+
return executableName;
241+
}
242+
return threadName;
243+
}
244+
235245
@Override
236246
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
237247
builder.startObject();
@@ -243,6 +253,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
243253
builder.field("annual_costs_usd", this.annualCostsUSD);
244254
builder.field("count", this.count);
245255
builder.field("executable_name", this.executableName);
256+
builder.field("thread_name", this.threadName);
246257
builder.endObject();
247258
return builder;
248259
}
@@ -258,7 +269,8 @@ public boolean equals(Object o) {
258269
&& Arrays.equals(fileIds, that.fileIds)
259270
&& Arrays.equals(frameIds, that.frameIds)
260271
&& Arrays.equals(typeIds, that.typeIds)
261-
&& executableName.equals(that.executableName);
272+
&& executableName.equals(that.executableName)
273+
&& threadName.equals(that.threadName);
262274
// Don't compare metadata like annualized co2, annualized costs, subGroups and count.
263275
}
264276

@@ -270,6 +282,7 @@ public int hashCode() {
270282
result = 31 * result + Arrays.hashCode(frameIds);
271283
result = 31 * result + Arrays.hashCode(typeIds);
272284
result = 31 * result + executableName.hashCode();
285+
result = 31 * result + threadName.hashCode();
273286
return result;
274287
}
275288
}

x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TraceEvent.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
final class TraceEvent {
1313
final String stacktraceID;
1414
String executableName;
15+
String threadName;
1516
double annualCO2Tons;
1617
double annualCostsUSD;
1718
long count;
@@ -25,6 +26,7 @@ final class TraceEvent {
2526
this.stacktraceID = stacktraceID;
2627
this.count = count;
2728
this.executableName = "";
29+
this.threadName = "";
2830
}
2931

3032
@Override
@@ -53,6 +55,9 @@ public String toString() {
5355
+ ", executableName='"
5456
+ executableName
5557
+ '\''
58+
+ ", threadName='"
59+
+ threadName
60+
+ '\''
5661
+ ", annualCO2Tons="
5762
+ annualCO2Tons
5863
+ ", annualCostsUSD="

x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TransportGetFlamegraphAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ static GetFlamegraphResponse buildFlamegraph(GetStackTracesResponse response) {
9494
annualCostsUSD,
9595
FRAMETYPE_EXECUTABLE
9696
);
97-
// builder.addOrUpdateAggregationNode(stackTrace.getThreadName(), samples, annualCO2Tons, annualCostsUSD, FRAMETYPE_THREAD);
97+
builder.addOrUpdateAggregationNode(stackTrace.getThreadName(), samples, annualCO2Tons, annualCostsUSD, FRAMETYPE_THREAD);
9898

9999
int frameCount = stackTrace.frameIds.length;
100100
for (int i = 0; i < frameCount; i++) {

x-pack/plugin/profiling/src/main/java/org/elasticsearch/xpack/profiling/action/TransportGetStackTracesAction.java

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,14 @@ private void searchEventGroupedByStackTrace(
337337
// Especially with high cardinality fields, this makes aggregations really slow.
338338
.executionHint("map")
339339
.subAggregation(groupByStackTraceId);
340+
TermsAggregationBuilder groupByThreadName = new TermsAggregationBuilder("group_by")
341+
// 'size' specifies the max number of host ID we support per request.
342+
.size(MAX_TRACE_EVENTS_RESULT_SIZE)
343+
.field("process.thread.name")
344+
// 'execution_hint: map' skips the slow building of ordinals that we don't need.
345+
// Especially with high cardinality fields, this makes aggregations really slow.
346+
.executionHint("map")
347+
.subAggregation(groupByHostId);
340348
SubGroupCollector subGroups = SubGroupCollector.attach(
341349
groupByStackTraceId,
342350
request.getAggregationFields(),
@@ -359,7 +367,7 @@ private void searchEventGroupedByStackTrace(
359367
// 'execution_hint: map' skips the slow building of ordinals that we don't need.
360368
// Especially with high cardinality fields, this makes aggregations really slow.
361369
.executionHint("map")
362-
.subAggregation(groupByHostId)
370+
.subAggregation(groupByThreadName)
363371
)
364372
.addAggregation(new SumAggregationBuilder("total_count").field("Stacktrace.count"))
365373
.execute(handleEventsGroupedByStackTrace(submitTask, client, responseBuilder, submitListener, searchResponse -> {
@@ -379,39 +387,45 @@ private void searchEventGroupedByStackTrace(
379387
for (Terms.Bucket executableBucket : executableNames.getBuckets()) {
380388
String executableName = executableBucket.getKeyAsString();
381389

382-
Terms hosts = executableBucket.getAggregations().get("group_by");
383-
for (Terms.Bucket hostBucket : hosts.getBuckets()) {
384-
String hostid = hostBucket.getKeyAsString();
385-
386-
Terms stacktraces = hostBucket.getAggregations().get("group_by");
387-
for (Terms.Bucket stacktraceBucket : stacktraces.getBuckets()) {
388-
Sum count = stacktraceBucket.getAggregations().get("count");
389-
int finalCount = resampler.adjustSampleCount((int) count.value());
390-
if (finalCount <= 0) {
391-
continue;
390+
Terms threads = executableBucket.getAggregations().get("group_by");
391+
for (Terms.Bucket threadBucket : threads.getBuckets()) {
392+
String threadName = threadBucket.getKeyAsString();
393+
394+
Terms hosts = threadBucket.getAggregations().get("group_by");
395+
for (Terms.Bucket hostBucket : hosts.getBuckets()) {
396+
String hostid = hostBucket.getKeyAsString();
397+
398+
Terms stacktraces = hostBucket.getAggregations().get("group_by");
399+
for (Terms.Bucket stacktraceBucket : stacktraces.getBuckets()) {
400+
Sum count = stacktraceBucket.getAggregations().get("count");
401+
int finalCount = resampler.adjustSampleCount((int) count.value());
402+
if (finalCount <= 0) {
403+
continue;
404+
}
405+
totalFinalCount += finalCount;
406+
407+
String stackTraceID = stacktraceBucket.getKeyAsString();
408+
409+
/*
410+
The same stacktraces may come from different hosts (eventually from different datacenters).
411+
We make a list of the triples here. As soon as we have the host metadata, we can calculate
412+
the CO2 emission and the costs for each TraceEvent.
413+
*/
414+
hostEventCounts.add(new HostEventCount(hostid, stackTraceID, finalCount));
415+
416+
TraceEvent event = stackTraceEvents.get(stackTraceID);
417+
if (event == null) {
418+
event = new TraceEvent(stackTraceID);
419+
event.executableName = executableName; // THIS IS A HACK (need a proper data model)
420+
event.threadName = threadName; // THIS IS A HACK (need a proper data model)
421+
stackTraceEvents.put(stackTraceID, event);
422+
}
423+
event.count += finalCount;
424+
subGroups.collectResults(stacktraceBucket, event);
425+
426+
TraceEventMetadata meta = new TraceEventMetadata(executableName, stackTraceID);
427+
executableEvents.putIfAbsent(meta, event);
392428
}
393-
totalFinalCount += finalCount;
394-
395-
String stackTraceID = stacktraceBucket.getKeyAsString();
396-
397-
/*
398-
The same stacktraces may come from different hosts (eventually from different datacenters).
399-
We make a list of the triples here. As soon as we have the host metadata, we can calculate
400-
the CO2 emission and the costs for each TraceEvent.
401-
*/
402-
hostEventCounts.add(new HostEventCount(hostid, stackTraceID, finalCount));
403-
404-
TraceEvent event = stackTraceEvents.get(stackTraceID);
405-
if (event == null) {
406-
event = new TraceEvent(stackTraceID);
407-
event.executableName = executableName; // THIS IS A HACK (need a proper data model)
408-
stackTraceEvents.put(stackTraceID, event);
409-
}
410-
event.count += finalCount;
411-
subGroups.collectResults(stacktraceBucket, event);
412-
413-
TraceEventMetadata meta = new TraceEventMetadata(executableName, stackTraceID);
414-
executableEvents.putIfAbsent(meta, event);
415429
}
416430
}
417431
}

0 commit comments

Comments
 (0)