Change profile stack element data structure (#4332)
* Change profile stack element to single level, not using tree
This commit is contained in:
parent
f29fc4bcb3
commit
e01829aa57
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
package org.apache.skywalking.oap.server.core.profile.analyze;
|
package org.apache.skywalking.oap.server.core.profile.analyze;
|
||||||
|
|
||||||
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackElement;
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackTree;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
@ -32,7 +32,7 @@ import java.util.stream.Collector;
|
||||||
/**
|
/**
|
||||||
* Work for {@link ProfileAnalyzer} to analyze.
|
* Work for {@link ProfileAnalyzer} to analyze.
|
||||||
*/
|
*/
|
||||||
public class ProfileAnalyzeCollector implements Collector<ProfileStack, ProfileStackNode, ProfileStackElement> {
|
public class ProfileAnalyzeCollector implements Collector<ProfileStack, ProfileStackNode, ProfileStackTree> {
|
||||||
@Override
|
@Override
|
||||||
public Supplier<ProfileStackNode> supplier() {
|
public Supplier<ProfileStackNode> supplier() {
|
||||||
return ProfileStackNode::newNode;
|
return ProfileStackNode::newNode;
|
||||||
|
|
@ -49,7 +49,7 @@ public class ProfileAnalyzeCollector implements Collector<ProfileStack, ProfileS
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Function<ProfileStackNode, ProfileStackElement> finisher() {
|
public Function<ProfileStackNode, ProfileStackTree> finisher() {
|
||||||
return ProfileStackNode::buildAnalyzeResult;
|
return ProfileStackNode::buildAnalyzeResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
package org.apache.skywalking.oap.server.core.profile.analyze;
|
package org.apache.skywalking.oap.server.core.profile.analyze;
|
||||||
|
|
||||||
import org.apache.skywalking.oap.server.core.query.entity.ProfileAnalyzation;
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileAnalyzation;
|
||||||
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackElement;
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackTree;
|
||||||
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
|
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
@ -45,13 +45,13 @@ public class ProfileAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// using parallel stream
|
// using parallel stream
|
||||||
Map<String, ProfileStackElement> stackTrees = stacks.parallelStream()
|
Map<String, ProfileStackTree> stackTrees = stacks.parallelStream()
|
||||||
// stack list cannot be empty
|
// stack list cannot be empty
|
||||||
.filter(s -> CollectionUtils.isNotEmpty(s.getStack()))
|
.filter(s -> CollectionUtils.isNotEmpty(s.getStack()))
|
||||||
.collect(Collectors.groupingBy(s -> s.getStack().get(0), ANALYZE_COLLECTOR));
|
.collect(Collectors.groupingBy(s -> s.getStack().get(0), ANALYZE_COLLECTOR));
|
||||||
|
|
||||||
ProfileAnalyzation analyzer = new ProfileAnalyzation();
|
ProfileAnalyzation analyzer = new ProfileAnalyzation();
|
||||||
analyzer.setStack(new ArrayList<>(stackTrees.values()));
|
analyzer.setTrees(new ArrayList<>(stackTrees.values()));
|
||||||
return analyzer;
|
return analyzer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ package org.apache.skywalking.oap.server.core.profile.analyze;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackElement;
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackElement;
|
||||||
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackTree;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Work for profiling stacks, intermediate state of the {@link ProfileStackElement} and {@link ProfileStack}
|
* Work for profiling stacks, intermediate state of the {@link ProfileStackElement} and {@link ProfileStack}
|
||||||
|
|
@ -149,10 +149,12 @@ public class ProfileStackNode {
|
||||||
* build GraphQL result, calculate duration and count data using parallels
|
* build GraphQL result, calculate duration and count data using parallels
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ProfileStackElement buildAnalyzeResult() {
|
public ProfileStackTree buildAnalyzeResult() {
|
||||||
// all nodes add to single-level list (such as flat), work for parallel calculating
|
// all nodes add to single-level list (such as flat), work for parallel calculating
|
||||||
LinkedList<Pair<ProfileStackElement, ProfileStackNode>> nodeMapping = new LinkedList<>();
|
LinkedList<Pair<ProfileStackElement, ProfileStackNode>> nodeMapping = new LinkedList<>();
|
||||||
ProfileStackElement root = buildElement();
|
int idGenerator = 1;
|
||||||
|
|
||||||
|
ProfileStackElement root = buildElement(idGenerator++);
|
||||||
nodeMapping.add(new Pair<>(root, this));
|
nodeMapping.add(new Pair<>(root, this));
|
||||||
|
|
||||||
// same with combine logic
|
// same with combine logic
|
||||||
|
|
@ -163,21 +165,24 @@ public class ProfileStackNode {
|
||||||
ProfileStackElement respElement = mergingPair.key;
|
ProfileStackElement respElement = mergingPair.key;
|
||||||
|
|
||||||
// generate children node and add to stack and all node mapping
|
// generate children node and add to stack and all node mapping
|
||||||
respElement.setChildren(mergingPair.value.children.stream().map(c -> {
|
for (ProfileStackNode children : mergingPair.value.children) {
|
||||||
ProfileStackElement element = c.buildElement();
|
ProfileStackElement element = children.buildElement(idGenerator++);
|
||||||
Pair<ProfileStackElement, ProfileStackNode> pair = new Pair<>(element, c);
|
element.setParentId(respElement.getId());
|
||||||
|
|
||||||
|
Pair<ProfileStackElement, ProfileStackNode> pair = new Pair<>(element, children);
|
||||||
stack.add(pair);
|
stack.add(pair);
|
||||||
nodeMapping.add(pair);
|
nodeMapping.add(pair);
|
||||||
|
}
|
||||||
return element;
|
|
||||||
}).collect(Collectors.toList()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate durations
|
// calculate durations
|
||||||
nodeMapping.parallelStream().forEach(t -> t.value.calculateDuration(t.key));
|
nodeMapping.parallelStream().forEach(t -> t.value.calculateDuration(t.key));
|
||||||
nodeMapping.parallelStream().forEach(t -> t.value.calculateDurationExcludeChild(t.key));
|
nodeMapping.parallelStream().forEach(t -> t.value.calculateDurationExcludeChild(t.key));
|
||||||
|
|
||||||
return root;
|
ProfileStackTree tree = new ProfileStackTree();
|
||||||
|
nodeMapping.forEach(n -> tree.getElements().add(n.key));
|
||||||
|
|
||||||
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void detectedBy(ProfileStack stack) {
|
private void detectedBy(ProfileStack stack) {
|
||||||
|
|
@ -188,10 +193,10 @@ public class ProfileStackNode {
|
||||||
this.detectedStacks.addAll(node.detectedStacks);
|
this.detectedStacks.addAll(node.detectedStacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProfileStackElement buildElement() {
|
private ProfileStackElement buildElement(int id) {
|
||||||
ProfileStackElement element = new ProfileStackElement();
|
ProfileStackElement element = new ProfileStackElement();
|
||||||
|
element.setId(id);
|
||||||
element.setCodeSignature(this.codeSignature);
|
element.setCodeSignature(this.codeSignature);
|
||||||
element.setChildren(new LinkedList<>());
|
|
||||||
element.setCount(this.detectedStacks.size());
|
element.setCount(this.detectedStacks.size());
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,11 @@ import java.util.List;
|
||||||
@Setter
|
@Setter
|
||||||
public class ProfileAnalyzation {
|
public class ProfileAnalyzation {
|
||||||
|
|
||||||
private List<ProfileStackElement> stack;
|
// if not empty means backend has information gave to the user
|
||||||
|
// such as: a large number of snapshots, only analyze part of the data
|
||||||
|
private String tip;
|
||||||
|
|
||||||
|
// thread stack dump analyze trees
|
||||||
|
private List<ProfileStackTree> trees;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,14 @@ package org.apache.skywalking.oap.server.core.query.entity;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author MrPro
|
|
||||||
*/
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class ProfileStackElement {
|
public class ProfileStackElement {
|
||||||
|
|
||||||
|
// work for tree building, id matches multiple parentId
|
||||||
|
private int id;
|
||||||
|
private int parentId;
|
||||||
|
|
||||||
// stack code signature
|
// stack code signature
|
||||||
private String codeSignature;
|
private String codeSignature;
|
||||||
|
|
||||||
|
|
@ -42,7 +41,4 @@ public class ProfileStackElement {
|
||||||
// continuous dump count
|
// continuous dump count
|
||||||
private int count;
|
private int count;
|
||||||
|
|
||||||
// children of this stack code sign
|
|
||||||
private List<ProfileStackElement> children;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.apache.skywalking.oap.server.core.query.entity;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class ProfileStackTree {
|
||||||
|
|
||||||
|
private List<ProfileStackElement> elements = new ArrayList<>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -36,9 +36,9 @@ public class ProfileStackAnalyze {
|
||||||
List<ProfileStack> stacks = data.transform();
|
List<ProfileStack> stacks = data.transform();
|
||||||
ProfileAnalyzation analyze = ProfileAnalyzer.analyze(stacks);
|
ProfileAnalyzation analyze = ProfileAnalyzer.analyze(stacks);
|
||||||
|
|
||||||
assertEquals(analyze.getStack().size(), expected.size());
|
assertEquals(analyze.getTrees().size(), expected.size());
|
||||||
for (int i = 0; i < analyze.getStack().size(); i++) {
|
for (int i = 0; i < analyze.getTrees().size(); i++) {
|
||||||
expected.get(i).verify(analyze.getStack().get(i));
|
expected.get(i).verify(analyze.getTrees().get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package org.apache.skywalking.oap.server.core.profile.bean;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackElement;
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackElement;
|
||||||
|
import org.apache.skywalking.oap.server.core.query.entity.ProfileStackTree;
|
||||||
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
|
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
|
@ -27,6 +28,7 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
|
@ -38,37 +40,56 @@ public class ProfileStackElementMatcher {
|
||||||
private String code;
|
private String code;
|
||||||
private String duration;
|
private String duration;
|
||||||
private int count;
|
private int count;
|
||||||
|
private int id = 0;
|
||||||
private List<ProfileStackElementMatcher> children;
|
private List<ProfileStackElementMatcher> children;
|
||||||
|
|
||||||
public void verify(ProfileStackElement element) {
|
public void verify(ProfileStackTree tree) {
|
||||||
|
// assert root
|
||||||
|
List<ProfileStackElement> fromParent = getFromParentId(tree, 0);
|
||||||
|
assertEquals(fromParent.size(), 1);
|
||||||
|
assertCurrentNode(fromParent.get(0));
|
||||||
|
|
||||||
|
setId(fromParent.get(0).getId());
|
||||||
|
assertChildren(tree, fromParent.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertChildren(ProfileStackTree tree, ProfileStackElement parent) {
|
||||||
|
List<ProfileStackElement> analyzedChildren = getFromParentId(tree, parent.getId());
|
||||||
|
|
||||||
|
if (CollectionUtils.isEmpty(analyzedChildren)) {
|
||||||
|
analyzedChildren = Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isEmpty(getChildren())) {
|
||||||
|
setChildren(Collections.emptyList());
|
||||||
|
}
|
||||||
|
assertEquals(analyzedChildren.size(), children.size());
|
||||||
|
|
||||||
|
// children code signature not sorted, need sort it, then verify
|
||||||
|
Collections.sort(children, Comparator.comparing(c -> c.code));
|
||||||
|
Collections.sort(analyzedChildren, Comparator.comparing(c -> c.getCodeSignature()));
|
||||||
|
|
||||||
|
for (int i = 0; i < children.size(); i++) {
|
||||||
|
children.get(i).setId(analyzedChildren.get(i).getId());
|
||||||
|
children.get(i).assertCurrentNode(analyzedChildren.get(i));
|
||||||
|
children.get(i).assertChildren(tree, analyzedChildren.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProfileStackElement> getFromParentId(ProfileStackTree tree, int fromParentId) {
|
||||||
|
return tree.getElements().stream().filter(e -> e.getParentId() == fromParentId).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCurrentNode(ProfileStackElement element) {
|
||||||
// analyze duration
|
// analyze duration
|
||||||
Matcher durationInfo = DURATION_PATTERN.matcher(duration);
|
Matcher durationInfo = DURATION_PATTERN.matcher(duration);
|
||||||
Assert.assertTrue("duration field pattern not match", durationInfo.find());
|
Assert.assertTrue("duration field pattern not match", durationInfo.find());
|
||||||
int duration = Integer.parseInt(durationInfo.group(1));
|
int duration = Integer.parseInt(durationInfo.group(1));
|
||||||
int durationExcludeChild = Integer.parseInt(durationInfo.group(2));
|
int durationExcludeChild = Integer.parseInt(durationInfo.group(2));
|
||||||
|
|
||||||
// assert
|
|
||||||
assertEquals(code, element.getCodeSignature());
|
assertEquals(code, element.getCodeSignature());
|
||||||
assertEquals(duration, element.getDuration());
|
assertEquals(duration, element.getDuration());
|
||||||
assertEquals(durationExcludeChild, element.getDurationChildExcluded());
|
assertEquals(durationExcludeChild, element.getDurationChildExcluded());
|
||||||
assertEquals(count, element.getCount());
|
assertEquals(count, element.getCount());
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(children)) {
|
|
||||||
children = Collections.emptyList();
|
|
||||||
}
|
|
||||||
if (CollectionUtils.isEmpty(element.getChildren())) {
|
|
||||||
element.setChildren(Collections.emptyList());
|
|
||||||
}
|
|
||||||
assertEquals(children.size(), element.getChildren().size());
|
|
||||||
|
|
||||||
// children code signature not sorted, need sort it, then verify
|
|
||||||
Collections.sort(children, Comparator.comparing(c -> c.code));
|
|
||||||
Collections.sort(element.getChildren(), Comparator.comparing(c -> c.getCodeSignature()));
|
|
||||||
|
|
||||||
for (int i = 0; i < children.size(); i++) {
|
|
||||||
children.get(i).verify(element.getChildren().get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ public class ProfileQuery implements GraphQLQueryResolver {
|
||||||
|
|
||||||
public ProfileAnalyzation getProfileAnalyze(final String segmentId, final long start, final long end) {
|
public ProfileAnalyzation getProfileAnalyze(final String segmentId, final long start, final long end) {
|
||||||
ProfileAnalyzation analyzation = new ProfileAnalyzation();
|
ProfileAnalyzation analyzation = new ProfileAnalyzation();
|
||||||
analyzation.setStack(Collections.emptyList());
|
analyzation.setTrees(Collections.emptyList());
|
||||||
return analyzation;
|
return analyzation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit f4e314de312a5bc053c6dbd6a07b561885b33888
|
Subproject commit 6104df8f0debce4c81142e5b1fc696d61bb673c5
|
||||||
Loading…
Reference in New Issue