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.Cpu; 20 import ohos.devtools.views.applicationtrace.bean.Func; 21 import ohos.devtools.views.applicationtrace.bean.Thread; 22 import ohos.devtools.views.applicationtrace.bean.TreeTableBean; 23 import ohos.devtools.views.applicationtrace.util.TimeUtils; 24 import ohos.devtools.views.trace.bean.Process; 25 26 import javax.swing.tree.DefaultMutableTreeNode; 27 import javax.swing.tree.TreeNode; 28 import java.util.ArrayList; 29 import java.util.Enumeration; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Objects; 34 import java.util.concurrent.TimeUnit; 35 import java.util.function.Consumer; 36 import java.util.stream.Collectors; 37 38 import static java.util.stream.Collectors.groupingBy; 39 40 /** 41 * app data 42 * 43 * @since 2021/5/20 18:00 44 */ 45 public class AllData { 46 /** 47 * cpu data map 48 */ 49 public static final Map<Integer, List<Cpu>> CPU_MAP = new HashMap<>(); 50 51 /** 52 * thread data map 53 */ 54 public static final Map<Integer, List<Thread>> THREAD_MAP = new HashMap<>(); 55 56 /** 57 * function data map 58 */ 59 public static final Map<Integer, List<Func>> FUNC_MAP = new HashMap<>(); 60 61 /** 62 * function names map 63 */ 64 public static Map<Integer, String> threadNames = new HashMap<>(); 65 66 /** 67 * list of process data 68 */ 69 protected static List<Process> processes = new ArrayList<>(); 70 71 /** 72 * get right TopDown tree by time range 73 * 74 * @param startNS startNS 75 * @param endNS endNS 76 * @return node List 77 */ getFuncTreeTopDown(long startNS, long endNS)78 public static List<DefaultMutableTreeNode> getFuncTreeTopDown(long startNS, long endNS) { 79 if (Objects.isNull(FUNC_MAP)) { 80 return new ArrayList<>(); 81 } 82 if (Objects.isNull(THREAD_MAP)) { 83 return new ArrayList<>(); 84 } 85 return getFuncTreeTopDown(startNS, endNS, null); 86 } 87 88 /** 89 * get right TopDown tree by time range and selected thread 90 * 91 * @param startNS startNS 92 * @param endNS endNS 93 * @param threadIds threadIds 94 * @return node List 95 */ getFuncTreeTopDown(long startNS, long endNS, List<Integer> threadIds)96 public static List<DefaultMutableTreeNode> getFuncTreeTopDown(long startNS, long endNS, List<Integer> threadIds) { 97 if (Objects.isNull(FUNC_MAP)) { 98 return new ArrayList<>(); 99 } 100 if (Objects.isNull(THREAD_MAP)) { 101 return new ArrayList<>(); 102 } 103 Map<Integer, List<AppFunc>> collect = FUNC_MAP.entrySet().stream() 104 .collect(Collectors.toMap(Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue()))); 105 return DataProcess.getFuncTreeTopDown(collect, startNS, endNS, threadIds); 106 } 107 108 /** 109 * get right BottomUp tree by time range 110 * 111 * @param startNS startNS 112 * @param endNS endNS 113 * @return tree node List 114 */ getFuncTreeBottomUp(long startNS, long endNS)115 public static List<DefaultMutableTreeNode> getFuncTreeBottomUp(long startNS, long endNS) { 116 return getFuncTreeBottomUp(startNS, endNS, null); 117 } 118 119 /** 120 * get right BottomUp tree by time range and thread 121 * 122 * @param startNS startNS 123 * @param endNS endNS 124 * @param threadIds threadIds 125 * @return tree node List 126 */ getFuncTreeBottomUp(long startNS, long endNS, List<Integer> threadIds)127 public static List<DefaultMutableTreeNode> getFuncTreeBottomUp(long startNS, long endNS, List<Integer> threadIds) { 128 Map<Integer, List<AppFunc>> collect = FUNC_MAP.entrySet().stream() 129 .collect(Collectors.toMap(Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue()))); 130 return DataProcess.getFuncTreeBottomUp(collect, startNS, endNS, threadIds); 131 } 132 133 /** 134 * get right TopDown tree by selected func 135 * 136 * @param func func 137 * @return tree node List 138 */ getFuncTreeByFuncTopDown(Func func)139 public static List<DefaultMutableTreeNode> getFuncTreeByFuncTopDown(Func func) { 140 List<Func> collect = FUNC_MAP.get(func.getTid()).stream().filter( 141 item -> TimeUtils.isRangeCross(func.getStartTs(), func.getEndTs(), item.getStartTs(), item.getEndTs()) 142 && item.getDepth() > func.getDepth()).collect(Collectors.toList()); 143 Map<String, TreeTableBean> longTreeTableBeanMap = funcGroupByStackId(func, collect, null); 144 List<TreeTableBean> treeTableBeans = setNumForNodes(longTreeTableBeanMap); 145 TreeTableBean treeTableBean = new TreeTableBean(); 146 treeTableBean.setName(func.getFuncName()); 147 long totalUs = TimeUnit.NANOSECONDS.toMicros(func.getDur()); 148 treeTableBean.setThreadDur(totalUs); 149 treeTableBean.setTotalNum(totalUs); 150 long threadFuncDuration = TimeUnit.NANOSECONDS.toMicros( 151 collect.stream().filter(item -> item.getDepth() == func.getDepth() + 1).mapToLong(Func::getDur).sum()); 152 treeTableBean.setChildrenNS(func.getDur()); 153 treeTableBean.setChildrenNum(threadFuncDuration); 154 treeTableBean.setSelfNum(totalUs - threadFuncDuration); 155 DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(treeTableBean); 156 Map<String, DefaultMutableTreeNode> treeNodeMap = 157 treeTableBeans.stream().collect(Collectors.toMap(TreeTableBean::getBloodId, DefaultMutableTreeNode::new)); 158 treeTableBeans.forEach(listBean -> { 159 if (listBean.getParentBloodId().equals(func.getBloodId())) { 160 rootNode.add(treeNodeMap.get(listBean.getBloodId())); 161 } else { 162 if (treeNodeMap.containsKey(listBean.getParentBloodId())) { 163 treeNodeMap.get(listBean.getParentBloodId()).add(treeNodeMap.get(listBean.getBloodId())); 164 } 165 } 166 }); 167 ArrayList<DefaultMutableTreeNode> defaultMutableTreeNodes = new ArrayList<>(); 168 defaultMutableTreeNodes.add(rootNode); 169 return defaultMutableTreeNodes; 170 } 171 172 /** 173 * get right BottomUp tree by selected func 174 * 175 * @param func func 176 * @return tree node List 177 */ getFuncTreeByFuncBottomUp(Func func)178 public static List<DefaultMutableTreeNode> getFuncTreeByFuncBottomUp(Func func) { 179 ArrayList<DefaultMutableTreeNode> nodes = new ArrayList<>(); 180 List<Func> collect = FUNC_MAP.get(func.getTid()).stream().filter( 181 item -> TimeUtils.isRangeCross(func.getStartTs(), func.getEndTs(), item.getStartTs(), item.getEndTs())) 182 .collect(Collectors.toList()); 183 Map<String, List<String>> nameToId = new HashMap<>(); 184 Map<String, TreeTableBean> treeNodeMap = funcGroupByStackId(func, collect, nameToId); 185 setNumForNodes(treeNodeMap); 186 nameToId.forEach((name, ids) -> { 187 TreeTableBean treeTableBean = new TreeTableBean(TimeUnit.NANOSECONDS.toMicros(func.getDur())); 188 treeTableBean.setName(name); 189 long totalNum = 0L; 190 long childrenNum = 0L; 191 long selfNum = 0L; 192 for (String id : ids) { 193 TreeTableBean tableBean = treeNodeMap.get(id); 194 totalNum += tableBean.getTotalNum(); 195 childrenNum += tableBean.getChildrenNum(); 196 selfNum += tableBean.getSelfNum(); 197 } 198 treeTableBean.setTotalNum(totalNum); 199 treeTableBean.setSelfNum(selfNum); 200 treeTableBean.setChildrenNum(childrenNum); 201 DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(treeTableBean); 202 ids.forEach( 203 id -> recursionNode(rootNode, treeNodeMap.get(id).getParentBloodId(), threadNames.get(func.getTid()), 204 treeNodeMap, id)); 205 if (ids.stream().noneMatch(id -> 206 collect.stream().filter(item -> item.getBloodId().equals(id) && item.getDepth() < func.getDepth()) 207 .toArray().length > 0)) { 208 nodes.add(rootNode); 209 } 210 }); 211 return nodes; 212 } 213 funcGroupByStackId(Func func, List<Func> collect, Map<String, List<String>> nameToId)214 private static Map<String, TreeTableBean> funcGroupByStackId(Func func, List<Func> collect, 215 Map<String, List<String>> nameToId) { 216 long totalUs = TimeUnit.NANOSECONDS.toMicros(func.getDur()); 217 return collect.stream().filter(item -> !item.getBloodId().isEmpty()).collect(groupingBy(Func::getBloodId)) 218 .entrySet().stream().collect(Collectors.toMap(entry -> entry.getKey(), entry -> { 219 TreeTableBean uniteBean = new TreeTableBean(totalUs); 220 uniteBean.setBloodId(entry.getKey()); 221 if (entry.getValue().size() > 0) { 222 uniteBean.setName(entry.getValue().get(0).getFuncName()); 223 uniteBean.setParentBloodId(entry.getValue().get(0).getParentBloodId()); 224 if (nameToId != null) { 225 if (nameToId.containsKey(entry.getValue().get(0).getFuncName())) { 226 nameToId.get(entry.getValue().get(0).getFuncName()) 227 .add(entry.getValue().get(0).getBloodId()); 228 } else { 229 List<String> ids = new ArrayList<>(); 230 ids.add(entry.getValue().get(0).getBloodId()); 231 nameToId.put(entry.getValue().get(0).getFuncName(), ids); 232 } 233 } 234 } 235 long childrenTotal = entry.getValue().stream().mapToLong(mapper -> TimeUtils 236 .getIntersection(func.getStartTs(), func.getEndTs(), mapper.getStartTs(), mapper.getEndTs())).sum(); 237 uniteBean.setTotalNum(childrenTotal); 238 uniteBean.setChildrenNS(entry.getValue().stream().mapToLong(mapper -> TimeUtils 239 .getNanoIntersection(func.getStartTs(), func.getEndTs(), mapper.getStartTs(), mapper.getEndTs())) 240 .sum()); 241 return uniteBean; 242 })); 243 } 244 245 /** 246 * set nodes data 247 * 248 * @param map map 249 * @return set node userObject 250 */ setNumForNodes(Map<String, TreeTableBean> map)251 public static List<TreeTableBean> setNumForNodes(Map<String, TreeTableBean> map) { // Set up presentation data 252 List<TreeTableBean> treeNodes = new ArrayList<>(map.values()); // Sort the array 253 for (TreeTableBean ts : treeNodes) { // Loop set children and total data 254 ts.setSelfNum(ts.getTotalNum() - ts.getChildrenNum()); 255 if (map.containsKey(ts.getParentBloodId())) { 256 TreeTableBean mapUserObject = map.get(ts.getParentBloodId()); 257 mapUserObject.setChildrenNum(mapUserObject.getChildrenNum() + ts.getTotalNum()); 258 mapUserObject.setSelfNum(mapUserObject.getTotalNum() - mapUserObject.getChildrenNum()); 259 } 260 } 261 return treeNodes; 262 } 263 recursionNode(DefaultMutableTreeNode rootNode, String parentId, String threadName, Map<String, TreeTableBean> treeNodeMap, String id)264 private static void recursionNode(DefaultMutableTreeNode rootNode, String parentId, String threadName, 265 Map<String, TreeTableBean> treeNodeMap, String id) { 266 if (rootNode.getUserObject() instanceof TreeTableBean) { 267 TreeTableBean topBean = (TreeTableBean) rootNode.getUserObject(); 268 TreeTableBean timeBean = treeNodeMap.get(id); 269 if (parentId.equals("")) { // Leaf node 270 recursionNodeLeaf(threadName, rootNode, topBean, timeBean); 271 } else { // Non-leaf nodes 272 Map<String, String> Ids = new HashMap(); 273 Ids.put("parentId", parentId); 274 Ids.put("id", id); 275 recursionNodeNonLeaf(threadName, rootNode, timeBean, treeNodeMap, Ids); 276 } 277 } 278 } 279 recursionNodeLeaf(String threadName, DefaultMutableTreeNode rootNode, TreeTableBean topBean, TreeTableBean timeBean)280 private static void recursionNodeLeaf(String threadName, DefaultMutableTreeNode rootNode, TreeTableBean topBean, 281 TreeTableBean timeBean) { 282 if (rootNode.getChildCount() != 0) { // The child node is thread and there are currently no child nodes 283 TreeNode tNode = rootNode.getChildAt(rootNode.getChildCount() - 1); 284 if (tNode instanceof DefaultMutableTreeNode) { 285 DefaultMutableTreeNode leafNode = (DefaultMutableTreeNode) tNode; 286 if (leafNode.getUserObject() instanceof TreeTableBean) { 287 TreeTableBean leafNodeUserObject = (TreeTableBean) leafNode.getUserObject(); 288 leafNodeUserObject.mergeTime(timeBean); 289 leafNode.setUserObject(leafNodeUserObject); 290 } 291 } 292 } 293 } 294 recursionNodeNonLeaf(String threadName, DefaultMutableTreeNode rootNode, TreeTableBean timeBean, Map<String, TreeTableBean> treeNodeMap, Map<String, String> Ids)295 private static void recursionNodeNonLeaf(String threadName, DefaultMutableTreeNode rootNode, TreeTableBean timeBean, 296 Map<String, TreeTableBean> treeNodeMap, Map<String, String> Ids) { 297 final TreeTableBean idBean = treeNodeMap.get(Ids.get("parentId")); 298 boolean sameName = false; 299 Enumeration<TreeNode> enumeration = rootNode.children(); 300 while (enumeration.hasMoreElements()) { 301 /* Compare whether there are node names in the current hierarchy that need to be merged */ 302 TreeNode nodeObj = enumeration.nextElement(); 303 if (nodeObj instanceof DefaultMutableTreeNode) { 304 DefaultMutableTreeNode nextElement = (DefaultMutableTreeNode) nodeObj; 305 if (nextElement.getUserObject() instanceof TreeTableBean) { 306 TreeTableBean nextElementUserObject = (TreeTableBean) nextElement.getUserObject(); 307 if (nextElementUserObject.getName().equals(idBean.getName())) { // The merge time difference 308 nextElementUserObject.mergeTime(timeBean); 309 recursionNode(nextElement, idBean.getParentBloodId(), threadName, treeNodeMap, Ids.get("id")); 310 sameName = true; 311 } 312 } 313 } 314 } 315 if (!sameName) { // No same node needs to be merged 316 TreeTableBean bean = new TreeTableBean(idBean.getThreadDur()); 317 bean.setName(idBean.getName()); 318 bean.setTime(timeBean); 319 DefaultMutableTreeNode addNode = new DefaultMutableTreeNode(bean); 320 rootNode.add(addNode); 321 recursionNode(addNode, idBean.getParentBloodId(), threadName, treeNodeMap, Ids.get("id")); 322 } 323 } 324 325 /** 326 * get right flame chart data by time range 327 * 328 * @param startNS startNS 329 * @param endNS endNS 330 * @return return flame node list 331 */ getFuncTreeFlameChart(long startNS, long endNS)332 public static List<DefaultMutableTreeNode> getFuncTreeFlameChart(long startNS, long endNS) { 333 List<DefaultMutableTreeNode> funcTreeTopDown = getFuncTreeTopDown(startNS, endNS); 334 funcTreeTopDown.forEach(AllData::resortNode); 335 sortNodeList(funcTreeTopDown); 336 return funcTreeTopDown; 337 } 338 339 /** 340 * get right flame chart data by selected func 341 * 342 * @param func func 343 * @return return flame node list 344 */ getFuncTreeFlameChart(Func func)345 public static List<DefaultMutableTreeNode> getFuncTreeFlameChart(Func func) { 346 return getFuncTreeByFuncTopDown(func); 347 } 348 349 /** 350 * get right flame chart data by time range and selected thread 351 * 352 * @param startNS startNS 353 * @param endNS endNS 354 * @param threadIds threadIds 355 * @return return flame node list 356 */ getFuncTreeFlameChart(long startNS, long endNS, List<Integer> threadIds)357 public static List<DefaultMutableTreeNode> getFuncTreeFlameChart(long startNS, long endNS, 358 List<Integer> threadIds) { 359 List<DefaultMutableTreeNode> funcTreeTopDown = getFuncTreeTopDown(startNS, endNS, threadIds); 360 funcTreeTopDown.forEach(AllData::resortNode); 361 sortNodeList(funcTreeTopDown); 362 return funcTreeTopDown; 363 } 364 resortNode(DefaultMutableTreeNode root)365 private static void resortNode(DefaultMutableTreeNode root) { 366 Consumer<DefaultMutableTreeNode> sort = parent -> { 367 Enumeration<TreeNode> children = parent.children(); 368 List<DefaultMutableTreeNode> childs = new ArrayList<>(); 369 while (children.hasMoreElements()) { 370 TreeNode node = children.nextElement(); 371 if (node instanceof DefaultMutableTreeNode) { 372 childs.add((DefaultMutableTreeNode) node); 373 } 374 } 375 parent.removeAllChildren(); 376 sortNodeList(childs).forEach(parent::add); 377 }; 378 Enumeration enumeration = root.depthFirstEnumeration(); 379 while (enumeration.hasMoreElements()) { 380 Object nodeObj = enumeration.nextElement(); 381 if (nodeObj instanceof DefaultMutableTreeNode) { 382 DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodeObj; 383 if (!node.isLeaf() && node.getChildCount() > 1) { 384 sort.accept(node); 385 } 386 } 387 } 388 } 389 sortNodeList(List<DefaultMutableTreeNode> list)390 private static List<DefaultMutableTreeNode> sortNodeList(List<DefaultMutableTreeNode> list) { 391 return list.stream().sorted((child1, child2) -> { 392 if (child1.getUserObject() instanceof TreeTableBean && child2.getUserObject() instanceof TreeTableBean) { 393 TreeTableBean bean1 = (TreeTableBean) child1.getUserObject(); 394 TreeTableBean bean2 = (TreeTableBean) child2.getUserObject(); 395 return Long.compare(bean1.getTotalNum(), bean2.getTotalNum()); 396 } 397 return 0; 398 }).collect(Collectors.toList()); 399 } 400 401 /** 402 * clear all static data 403 */ 404 public static void clearData() { 405 if (CPU_MAP != null) { 406 CPU_MAP.values().forEach(List::clear); 407 CPU_MAP.clear(); 408 } 409 if (THREAD_MAP != null) { 410 THREAD_MAP.values().forEach(List::clear); 411 THREAD_MAP.clear(); 412 } 413 if (FUNC_MAP != null) { 414 FUNC_MAP.values().forEach(List::clear); 415 FUNC_MAP.clear(); 416 } 417 if (threadNames != null) { 418 threadNames.clear(); 419 } 420 if (processes != null) { 421 processes.clear(); 422 } 423 } 424 425 } 426