Change profile stack element data structure (#4332)

* Change profile stack element to single level, not using tree
This commit is contained in:
mrproliu 2020-02-09 16:37:45 +08:00 committed by GitHub
parent f29fc4bcb3
commit e01829aa57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 110 additions and 51 deletions

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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<>();
}

View File

@ -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));
} }
} }

View File

@ -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));
}
} }
} }

View File

@ -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