• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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