1 /* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package ohos.devtools.views.applicationtrace; 17 18 import ohos.devtools.views.applicationtrace.bean.AppFunc; 19 import ohos.devtools.views.applicationtrace.bean.TreeTableBean; 20 import ohos.devtools.views.applicationtrace.util.TimeUtils; 21 22 import javax.swing.tree.DefaultMutableTreeNode; 23 import javax.swing.tree.TreeNode; 24 import java.util.ArrayList; 25 import java.util.Enumeration; 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Objects; 30 import java.util.concurrent.TimeUnit; 31 import java.util.stream.Collectors; 32 33 import static java.util.stream.Collectors.groupingBy; 34 35 /** 36 * DataProcess 37 * 38 * @since 2021/5/12 16:34 39 */ 40 public class DataProcess { 41 /** 42 * get the get TopDown FuncTree by startNS、endNS and threadIds 43 * 44 * @param funcMap funcMap 45 * @param startNS startNS 46 * @param endNS endNS 47 * @param threadIds threadIds 48 * @return list <DefaultMutableTreeNode> nodes 49 */ getFuncTreeTopDown(Map<Integer, List<AppFunc>> funcMap, long startNS, long endNS, List<Integer> threadIds)50 public static List<DefaultMutableTreeNode> getFuncTreeTopDown(Map<Integer, List<AppFunc>> funcMap, long startNS, 51 long endNS, List<Integer> threadIds) { 52 if (Objects.isNull(funcMap)) { 53 return new ArrayList<>(); 54 } 55 List<AppFunc> funcs = 56 funcMap.entrySet().stream().filter(entry -> threadIds == null || threadIds.contains(entry.getKey())) 57 .flatMap(entry -> entry.getValue().stream()).collect(Collectors.toList()); 58 Map<String, TreeTableBean> map = funcGroupByStackId(startNS, endNS, funcs, null); 59 List<TreeTableBean> treeTableBeans = setNumForNodes(map); 60 Map<String, DefaultMutableTreeNode> treeNodeMap = treeTableBeans.stream() 61 .collect(Collectors.toMap(TreeTableBean::getPrefStackId, DefaultMutableTreeNode::new)); 62 treeTableBeans.forEach(treeTableBean -> { 63 if (!treeTableBean.getPrefParentStackId().isEmpty()) { 64 if (treeNodeMap.containsKey(treeTableBean.getPrefParentStackId())) { 65 treeNodeMap.get(treeTableBean.getPrefParentStackId()) 66 .add(treeNodeMap.get(treeTableBean.getPrefStackId())); 67 } 68 } 69 }); 70 return treeNodeMap.values().stream().filter(node -> { 71 if (node.getUserObject() instanceof TreeTableBean) { 72 return ((TreeTableBean) node.getUserObject()).getPrefParentStackId().isEmpty(); 73 } 74 return false; 75 }).collect(Collectors.toList()); 76 } 77 78 /** 79 * get the get BottomUp FuncTree by startNS、endNS and threadIds 80 * 81 * @param funcMap funcMap 82 * @param startNS startNS 83 * @param endNS endNS 84 * @param threadIds threadIds 85 * @return list <DefaultMutableTreeNode> nodes 86 */ 87 public static List<DefaultMutableTreeNode> getFuncTreeBottomUp(Map<Integer, List<AppFunc>> funcMap, long startNS, 88 long endNS, List<Integer> threadIds) { 89 long dur = TimeUnit.NANOSECONDS.toMicros(endNS - startNS); 90 ArrayList<DefaultMutableTreeNode> nodes = new ArrayList<>(); 91 Map<String, List<String>> nameToId = new HashMap<>(); 92 List<AppFunc> funcs = 93 funcMap.entrySet().stream().filter(entry -> threadIds == null || threadIds.contains(entry.getKey())) 94 .flatMap(entry -> entry.getValue().stream()).collect(Collectors.toList()); 95 Map<String, TreeTableBean> treeNodeMap = funcGroupByStackId(startNS, endNS, funcs, nameToId); 96 setNumForNodes(treeNodeMap); 97 nameToId.forEach((name, ids) -> { 98 DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); 99 TreeTableBean treeTableBean = new TreeTableBean(dur); 100 treeTableBean.setName(name); 101 long totalNum = 0L; 102 long childrenNum = 0L; 103 long selfNum = 0L; 104 for (String id : ids) { 105 TreeTableBean tableBean = treeNodeMap.get(id); 106 totalNum += tableBean.getTotalNum(); 107 childrenNum += tableBean.getChildrenNum(); 108 selfNum += tableBean.getSelfNum(); 109 } 110 treeTableBean.setTotalNum(totalNum); 111 treeTableBean.setSelfNum(selfNum); 112 treeTableBean.setChildrenNum(childrenNum); 113 rootNode.setUserObject(treeTableBean); 114 ids.forEach(id -> recursionNode(rootNode, treeNodeMap.get(id).getPrefParentStackId(), treeNodeMap, id)); 115 nodes.add(rootNode); 116 }); 117 return nodes; 118 } 119 120 private static Map<String, TreeTableBean> funcGroupByStackId(long startNS, long endNS, List<AppFunc> list, 121 Map<String, List<String>> nameToId) { // Group by stacked 122 long dur = TimeUnit.NANOSECONDS.toMicros(endNS - startNS); 123 List<Map.Entry<String, List<AppFunc>>> list1 = list.stream().filter(func -> { 124 long funcEndTs = func.getEndTs(); 125 long funcStartTs = func.getStartTs(); 126 return funcEndTs >= startNS && funcStartTs <= endNS; 127 }).collect(groupingBy(AppFunc::getBloodId)).entrySet().stream().collect(Collectors.toList()); 128 return list1.stream().collect(Collectors.toMap(Map.Entry::getKey, a1 -> { 129 TreeTableBean uniteBean = new TreeTableBean(dur); 130 uniteBean.setThreadDur(dur); 131 uniteBean.setPrefStackId(a1.getKey()); 132 if (a1.getValue().size() > 0) { 133 uniteBean.setName(a1.getValue().get(0).getFuncName()); 134 uniteBean.setPrefParentStackId(a1.getValue().get(0).getParentBloodId()); 135 if (nameToId != null) { 136 if (nameToId.containsKey(a1.getValue().get(0).getFuncName())) { 137 nameToId.get(a1.getValue().get(0).getFuncName()).add(a1.getValue().get(0).getBloodId()); 138 } else { 139 List<String> arrayList = new ArrayList<>(); 140 arrayList.add(a1.getValue().get(0).getBloodId()); 141 nameToId.put(a1.getValue().get(0).getFuncName(), arrayList); 142 } 143 } 144 } 145 long childrenTotal = a1.getValue().stream() 146 .mapToLong(child -> TimeUtils.getIntersection(startNS, endNS, child.getStartTs(), child.getEndTs())) 147 .sum(); 148 uniteBean.setTotalNum(childrenTotal); 149 uniteBean.setChildrenNS(a1.getValue().stream() 150 .mapToLong(child -> TimeUtils.getNanoIntersection(startNS, endNS, child.getStartTs(), child.getEndTs())) 151 .sum()); 152 return uniteBean; 153 })); 154 } 155 156 /** 157 * Set up presentation data 158 * 159 * @param map map 160 * @return list <TreeTableBean> 161 */ 162 private static List<TreeTableBean> setNumForNodes(Map<String, TreeTableBean> map) { 163 List<TreeTableBean> treeNodes = new ArrayList<>(map.values()); // Sort the array 164 for (TreeTableBean ts : treeNodes) { // Loop set children and total data 165 ts.setSelfNum(ts.getTotalNum() - ts.getChildrenNum()); 166 if (map.containsKey(ts.getPrefParentStackId())) { 167 TreeTableBean mapUserObject = map.get(ts.getPrefParentStackId()); 168 mapUserObject.setChildrenNum(mapUserObject.getChildrenNum() + ts.getTotalNum()); 169 mapUserObject.setSelfNum(mapUserObject.getTotalNum() - mapUserObject.getChildrenNum()); 170 } 171 } 172 return treeNodes; 173 } 174 175 private static void recursionNode(DefaultMutableTreeNode rootNode, String parentId, 176 Map<String, TreeTableBean> treeNodeMap, String id) { 177 if (rootNode.getUserObject() instanceof TreeTableBean) { 178 TreeTableBean topBean = (TreeTableBean) rootNode.getUserObject(); 179 TreeTableBean timeBean = treeNodeMap.get(id); 180 if (parentId.isEmpty()) { // Leaf node 181 recursionNodeLeaf(rootNode, timeBean); 182 } else { // Non-leaf nodes 183 Map<String, String> Ids = new HashMap(); 184 Ids.put("parentId", parentId); 185 Ids.put("id", id); 186 recursionNodeNonLeaf(rootNode, timeBean, topBean, treeNodeMap, Ids); 187 } 188 } 189 } 190 191 private static void recursionNodeNonLeaf(DefaultMutableTreeNode rootNode, TreeTableBean timeBean, 192 TreeTableBean topBean, Map<String, TreeTableBean> treeNodeMap, Map<String, String> Ids) { 193 final TreeTableBean idBean = treeNodeMap.get(Ids.get("parentId")); 194 boolean sameName = false; 195 Enumeration<TreeNode> enumeration = rootNode.children(); 196 while (enumeration.hasMoreElements()) { 197 /* Compare whether there are node names in the current hierarchy that need to be merged */ 198 TreeNode treeNode = enumeration.nextElement(); 199 if (treeNode instanceof DefaultMutableTreeNode) { 200 DefaultMutableTreeNode nextElement = (DefaultMutableTreeNode) treeNode; 201 if (nextElement.getUserObject() instanceof TreeTableBean) { 202 TreeTableBean nextElementUserObject = (TreeTableBean) nextElement.getUserObject(); 203 if (nextElementUserObject.getName().equals(idBean.getName())) { // The merge time difference 204 nextElementUserObject.mergeTime(timeBean); 205 recursionNode(nextElement, idBean.getPrefParentStackId(), treeNodeMap, Ids.get("id")); 206 sameName = true; 207 } 208 } 209 } 210 } 211 if (!sameName) { // No same node needs to be merged 212 DefaultMutableTreeNode addNode = new DefaultMutableTreeNode(); 213 TreeTableBean treeTableBean = new TreeTableBean(topBean.getThreadDur()); 214 treeTableBean.setName(idBean.getName()); 215 treeTableBean.setTime(timeBean); 216 addNode.setUserObject(treeTableBean); 217 rootNode.add(addNode); 218 recursionNode(addNode, idBean.getPrefParentStackId(), treeNodeMap, Ids.get("id")); 219 } 220 } 221 222 private static void recursionNodeLeaf(DefaultMutableTreeNode rootNode, TreeTableBean timeBean) { 223 if (rootNode.getChildCount() != 0) { // The child node is thread and there are currently no child nodes 224 TreeNode child = rootNode.getChildAt(rootNode.getChildCount() - 1); 225 if (child instanceof DefaultMutableTreeNode) { 226 DefaultMutableTreeNode leafNode = (DefaultMutableTreeNode) child; 227 if (leafNode.getUserObject() instanceof TreeTableBean) { 228 TreeTableBean leafNodeUserObject = (TreeTableBean) leafNode.getUserObject(); 229 leafNodeUserObject.mergeTime(timeBean); 230 leafNode.setUserObject(leafNodeUserObject); 231 } 232 } 233 } 234 } 235 } 236