/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed 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 ohos.devtools.views.applicationtrace; import ohos.devtools.views.applicationtrace.bean.AppFunc; import ohos.devtools.views.applicationtrace.bean.TreeTableBean; import ohos.devtools.views.applicationtrace.util.TimeUtils; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeNode; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static java.util.stream.Collectors.groupingBy; /** * DataProcess * * @since 2021/5/12 16:34 */ public class DataProcess { /** * get the get TopDown FuncTree by startNS、endNS and threadIds * * @param funcMap funcMap * @param startNS startNS * @param endNS endNS * @param threadIds threadIds * @return list nodes */ public static List getFuncTreeTopDown(Map> funcMap, long startNS, long endNS, List threadIds) { if (Objects.isNull(funcMap)) { return new ArrayList<>(); } List funcs = funcMap.entrySet().stream().filter(entry -> threadIds == null || threadIds.contains(entry.getKey())) .flatMap(entry -> entry.getValue().stream()).collect(Collectors.toList()); Map map = funcGroupByStackId(startNS, endNS, funcs, null); List treeTableBeans = setNumForNodes(map); Map treeNodeMap = treeTableBeans.stream() .collect(Collectors.toMap(TreeTableBean::getPrefStackId, DefaultMutableTreeNode::new)); treeTableBeans.forEach(treeTableBean -> { if (!treeTableBean.getPrefParentStackId().isEmpty()) { if (treeNodeMap.containsKey(treeTableBean.getPrefParentStackId())) { treeNodeMap.get(treeTableBean.getPrefParentStackId()) .add(treeNodeMap.get(treeTableBean.getPrefStackId())); } } }); return treeNodeMap.values().stream().filter(node -> { if (node.getUserObject() instanceof TreeTableBean) { return ((TreeTableBean) node.getUserObject()).getPrefParentStackId().isEmpty(); } return false; }).collect(Collectors.toList()); } /** * get the get BottomUp FuncTree by startNS、endNS and threadIds * * @param funcMap funcMap * @param startNS startNS * @param endNS endNS * @param threadIds threadIds * @return list nodes */ public static List getFuncTreeBottomUp(Map> funcMap, long startNS, long endNS, List threadIds) { long dur = TimeUnit.NANOSECONDS.toMicros(endNS - startNS); ArrayList nodes = new ArrayList<>(); Map> nameToId = new HashMap<>(); List funcs = funcMap.entrySet().stream().filter(entry -> threadIds == null || threadIds.contains(entry.getKey())) .flatMap(entry -> entry.getValue().stream()).collect(Collectors.toList()); Map treeNodeMap = funcGroupByStackId(startNS, endNS, funcs, nameToId); setNumForNodes(treeNodeMap); nameToId.forEach((name, ids) -> { DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); TreeTableBean treeTableBean = new TreeTableBean(dur); treeTableBean.setName(name); long totalNum = 0L; long childrenNum = 0L; long selfNum = 0L; for (String id : ids) { TreeTableBean tableBean = treeNodeMap.get(id); totalNum += tableBean.getTotalNum(); childrenNum += tableBean.getChildrenNum(); selfNum += tableBean.getSelfNum(); } treeTableBean.setTotalNum(totalNum); treeTableBean.setSelfNum(selfNum); treeTableBean.setChildrenNum(childrenNum); rootNode.setUserObject(treeTableBean); ids.forEach(id -> recursionNode(rootNode, treeNodeMap.get(id).getPrefParentStackId(), treeNodeMap, id)); nodes.add(rootNode); }); return nodes; } private static Map funcGroupByStackId(long startNS, long endNS, List list, Map> nameToId) { // Group by stacked long dur = TimeUnit.NANOSECONDS.toMicros(endNS - startNS); List>> list1 = list.stream().filter(func -> { long funcEndTs = func.getEndTs(); long funcStartTs = func.getStartTs(); return funcEndTs >= startNS && funcStartTs <= endNS; }).collect(groupingBy(AppFunc::getBloodId)).entrySet().stream().collect(Collectors.toList()); return list1.stream().collect(Collectors.toMap(Map.Entry::getKey, a1 -> { TreeTableBean uniteBean = new TreeTableBean(dur); uniteBean.setThreadDur(dur); uniteBean.setPrefStackId(a1.getKey()); if (a1.getValue().size() > 0) { uniteBean.setName(a1.getValue().get(0).getFuncName()); uniteBean.setPrefParentStackId(a1.getValue().get(0).getParentBloodId()); if (nameToId != null) { if (nameToId.containsKey(a1.getValue().get(0).getFuncName())) { nameToId.get(a1.getValue().get(0).getFuncName()).add(a1.getValue().get(0).getBloodId()); } else { List arrayList = new ArrayList<>(); arrayList.add(a1.getValue().get(0).getBloodId()); nameToId.put(a1.getValue().get(0).getFuncName(), arrayList); } } } long childrenTotal = a1.getValue().stream() .mapToLong(child -> TimeUtils.getIntersection(startNS, endNS, child.getStartTs(), child.getEndTs())) .sum(); uniteBean.setTotalNum(childrenTotal); uniteBean.setChildrenNS(a1.getValue().stream() .mapToLong(child -> TimeUtils.getNanoIntersection(startNS, endNS, child.getStartTs(), child.getEndTs())) .sum()); return uniteBean; })); } /** * Set up presentation data * * @param map map * @return list */ private static List setNumForNodes(Map map) { List treeNodes = new ArrayList<>(map.values()); // Sort the array for (TreeTableBean ts : treeNodes) { // Loop set children and total data ts.setSelfNum(ts.getTotalNum() - ts.getChildrenNum()); if (map.containsKey(ts.getPrefParentStackId())) { TreeTableBean mapUserObject = map.get(ts.getPrefParentStackId()); mapUserObject.setChildrenNum(mapUserObject.getChildrenNum() + ts.getTotalNum()); mapUserObject.setSelfNum(mapUserObject.getTotalNum() - mapUserObject.getChildrenNum()); } } return treeNodes; } private static void recursionNode(DefaultMutableTreeNode rootNode, String parentId, Map treeNodeMap, String id) { if (rootNode.getUserObject() instanceof TreeTableBean) { TreeTableBean topBean = (TreeTableBean) rootNode.getUserObject(); TreeTableBean timeBean = treeNodeMap.get(id); if (parentId.isEmpty()) { // Leaf node recursionNodeLeaf(rootNode, timeBean); } else { // Non-leaf nodes Map Ids = new HashMap(); Ids.put("parentId", parentId); Ids.put("id", id); recursionNodeNonLeaf(rootNode, timeBean, topBean, treeNodeMap, Ids); } } } private static void recursionNodeNonLeaf(DefaultMutableTreeNode rootNode, TreeTableBean timeBean, TreeTableBean topBean, Map treeNodeMap, Map Ids) { final TreeTableBean idBean = treeNodeMap.get(Ids.get("parentId")); boolean sameName = false; Enumeration enumeration = rootNode.children(); while (enumeration.hasMoreElements()) { /* Compare whether there are node names in the current hierarchy that need to be merged */ TreeNode treeNode = enumeration.nextElement(); if (treeNode instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode nextElement = (DefaultMutableTreeNode) treeNode; if (nextElement.getUserObject() instanceof TreeTableBean) { TreeTableBean nextElementUserObject = (TreeTableBean) nextElement.getUserObject(); if (nextElementUserObject.getName().equals(idBean.getName())) { // The merge time difference nextElementUserObject.mergeTime(timeBean); recursionNode(nextElement, idBean.getPrefParentStackId(), treeNodeMap, Ids.get("id")); sameName = true; } } } } if (!sameName) { // No same node needs to be merged DefaultMutableTreeNode addNode = new DefaultMutableTreeNode(); TreeTableBean treeTableBean = new TreeTableBean(topBean.getThreadDur()); treeTableBean.setName(idBean.getName()); treeTableBean.setTime(timeBean); addNode.setUserObject(treeTableBean); rootNode.add(addNode); recursionNode(addNode, idBean.getPrefParentStackId(), treeNodeMap, Ids.get("id")); } } private static void recursionNodeLeaf(DefaultMutableTreeNode rootNode, TreeTableBean timeBean) { if (rootNode.getChildCount() != 0) { // The child node is thread and there are currently no child nodes TreeNode child = rootNode.getChildAt(rootNode.getChildCount() - 1); if (child instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode leafNode = (DefaultMutableTreeNode) child; if (leafNode.getUserObject() instanceof TreeTableBean) { TreeTableBean leafNodeUserObject = (TreeTableBean) leafNode.getUserObject(); leafNodeUserObject.mergeTime(timeBean); leafNode.setUserObject(leafNodeUserObject); } } } } }